Repository: Microsoft/BotFramework-Samples Branch: master Commit: 234f3fb2e445 Files: 460 Total size: 1.8 MB Directory structure: gitextract_fa8vrjl3/ ├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md ├── SDKV4-Samples/ │ ├── .gitattributes │ ├── .gitignore │ ├── LICENSE │ ├── README.md │ ├── _config.yml │ ├── dotnet_core/ │ │ ├── ComplexDialogBot/ │ │ │ ├── BotBuilder.ruleset │ │ │ ├── ComplexDialogBot.cs │ │ │ ├── ComplexDialogBot.csproj │ │ │ ├── ComplexDialogBotAccessors.cs │ │ │ ├── DeploymentScripts/ │ │ │ │ └── MsbotClone/ │ │ │ │ └── bot.recipe │ │ │ ├── Program.cs │ │ │ ├── Properties/ │ │ │ │ └── launchSettings.json │ │ │ ├── README.md │ │ │ ├── Startup.cs │ │ │ ├── UserProfile.cs │ │ │ ├── appsettings.json │ │ │ ├── complex-dialog.bot │ │ │ └── wwwroot/ │ │ │ └── default.htm │ │ ├── DialogInterruptionsBot/ │ │ │ ├── BotBuilder.ruleset │ │ │ ├── DeploymentScripts/ │ │ │ │ └── MsbotClone/ │ │ │ │ └── bot.recipe │ │ │ ├── DialogInterruptionsBot.cs │ │ │ ├── DialogInterruptionsBot.csproj │ │ │ ├── DialogInterruptionsBotAccessors.cs │ │ │ ├── Program.cs │ │ │ ├── Properties/ │ │ │ │ └── launchSettings.json │ │ │ ├── README.md │ │ │ ├── Startup.cs │ │ │ ├── UserProfile.cs │ │ │ ├── appsettings.json │ │ │ ├── dialog-interruptions.bot │ │ │ └── wwwroot/ │ │ │ └── default.htm │ │ ├── DialogPromptBot/ │ │ │ ├── DeploymentScripts/ │ │ │ │ └── MsbotClone/ │ │ │ │ └── bot.recipe │ │ │ ├── DialogPromptBot.cs │ │ │ ├── DialogPromptBot.csproj │ │ │ ├── DialogPromptBot.ruleset │ │ │ ├── DialogPromptBot.xml │ │ │ ├── DialogPromptBotAccessors.cs │ │ │ ├── Program.cs │ │ │ ├── Properties/ │ │ │ │ └── launchSettings.json │ │ │ ├── README.md │ │ │ ├── Startup.cs │ │ │ ├── appsettings.json │ │ │ ├── dialog-prompt.bot │ │ │ └── wwwroot/ │ │ │ └── default.htm │ │ ├── PromptUsersForInput/ │ │ │ ├── ConversationFlow.cs │ │ │ ├── CustomPromptBot.cs │ │ │ ├── CustomPromptBot.ruleset │ │ │ ├── CustomPromptBotAccessors.cs │ │ │ ├── DeploymentScripts/ │ │ │ │ └── MsbotClone/ │ │ │ │ └── bot.recipe │ │ │ ├── Program.cs │ │ │ ├── PromptUsersForInput.csproj │ │ │ ├── Properties/ │ │ │ │ └── launchSettings.json │ │ │ ├── README.md │ │ │ ├── Startup.cs │ │ │ ├── UserProfile.cs │ │ │ ├── appsettings.json │ │ │ ├── custom-prompt.bot │ │ │ └── wwwroot/ │ │ │ └── default.htm │ │ ├── StateBot/ │ │ │ ├── ConversationData.cs │ │ │ ├── DeploymentScripts/ │ │ │ │ └── MsbotClone/ │ │ │ │ └── bot.recipe │ │ │ ├── Program.cs │ │ │ ├── Properties/ │ │ │ │ └── launchSettings.json │ │ │ ├── README.md │ │ │ ├── Startup.cs │ │ │ ├── StateBot.cs │ │ │ ├── StateBot.csproj │ │ │ ├── StateBot.ruleset │ │ │ ├── StateBot.sln │ │ │ ├── StateBot.xml │ │ │ ├── StateBotAccessors.cs │ │ │ ├── UserProfile.cs │ │ │ ├── appsettings.json │ │ │ ├── state.bot │ │ │ └── wwwroot/ │ │ │ └── default.htm │ │ ├── nlp-with-dispatch/ │ │ │ ├── QnAMaker.tsv │ │ │ ├── home-automation.json │ │ │ ├── nlp-with-dispatchDispatch.json │ │ │ └── weather.json │ │ └── nlp-with-luis/ │ │ └── reminders-with-entities.json │ └── js/ │ ├── DialogPromptBot/ │ │ ├── .eslintrc.js │ │ ├── .gitignore │ │ ├── README.md │ │ ├── bot.js │ │ ├── deploymentScripts/ │ │ │ └── msbotClone/ │ │ │ └── bot.recipe │ │ ├── dialog-prompt.bot │ │ ├── index.js │ │ └── package.json │ ├── PromptUsersForInput/ │ │ ├── .eslintrc.js │ │ ├── README.md │ │ ├── bot.js │ │ ├── deploymentScripts/ │ │ │ └── msbotClone/ │ │ │ └── bot.recipe │ │ ├── index.js │ │ ├── package.json │ │ ├── resources/ │ │ │ └── echo.chat │ │ └── simplePrompts.bot │ ├── complexDialogBot/ │ │ ├── .eslintrc.js │ │ ├── .gitignore │ │ ├── ComplexDialogBot.bot │ │ ├── README.md │ │ ├── bot.js │ │ ├── deploymentScripts/ │ │ │ └── msbotClone/ │ │ │ └── bot.recipe │ │ ├── iisnode.yml │ │ ├── index.js │ │ ├── package.json │ │ └── web.config │ ├── nlp-with-dispatch/ │ │ ├── QnAMaker.tsv │ │ ├── home-automation.json │ │ ├── nlp-with-dispatchDispatch.json │ │ └── weather.json │ ├── nlp-with-luis/ │ │ └── reminders-with-entities.json │ └── stateBot/ │ ├── .eslintrc.js │ ├── .gitignore │ ├── README.md │ ├── bot.js │ ├── deploymentScripts/ │ │ └── msbotClone/ │ │ └── bot.recipe │ ├── index.js │ ├── package.json │ ├── resources/ │ │ └── echo.chat │ └── stateBot.bot ├── SECURITY.md ├── StackOverflow-Bot/ │ ├── .gitignore │ ├── DialogAnalyzerFunc/ │ │ ├── AnalyzeDialog.cs │ │ ├── Clients/ │ │ │ ├── DialogAnalyzerClient.cs │ │ │ └── DialogDataInterpreter.cs │ │ ├── DialogAnalyzerFunc.csproj │ │ ├── Extensions/ │ │ │ ├── EnumerableExtensions.cs │ │ │ └── HttpExtensions.cs │ │ ├── Models/ │ │ │ ├── ComputerVisionImageAnalysisResult.cs │ │ │ ├── DialogAnalysisResult.cs │ │ │ ├── HandwritingRecognitionResult.cs │ │ │ ├── ImageTextRegion.cs │ │ │ └── TextAnalyticsResult.cs │ │ ├── Services/ │ │ │ ├── ComputerVisionService.cs │ │ │ └── TextAnalyticsService.cs │ │ ├── Utilities/ │ │ │ ├── HttpClientUtility.cs │ │ │ └── StringUtility.cs │ │ └── host.json │ ├── LICENSE │ ├── README.md │ ├── StackBot/ │ │ ├── Dockerfile │ │ ├── StackBot.njsproj │ │ ├── data/ │ │ │ ├── jokes.json │ │ │ ├── luis.json │ │ │ └── smalltalk.tsv │ │ ├── dialogs/ │ │ │ ├── brain.js │ │ │ ├── joke.js │ │ │ ├── keywordPrompt.js │ │ │ ├── languages.js │ │ │ ├── menu.js │ │ │ ├── screenshot.js │ │ │ ├── search.js │ │ │ └── smalltalk.js │ │ ├── index.js │ │ ├── lib/ │ │ │ ├── attachments.js │ │ │ ├── bingsearchclient.js │ │ │ ├── cognitiveservices.js │ │ │ ├── dialoganalyzerclient.js │ │ │ ├── qnaclient.js │ │ │ ├── sentimentanalyzerclient.js │ │ │ └── smalltalk.js │ │ ├── package.json │ │ └── static/ │ │ └── index.html │ ├── StackCode/ │ │ ├── StackCode-0.1.1.vsix │ │ ├── out/ │ │ │ ├── src/ │ │ │ │ ├── bot/ │ │ │ │ │ └── bot.html │ │ │ │ └── extension.js │ │ │ └── test/ │ │ │ ├── extension.test.js │ │ │ └── index.js │ │ ├── package.json │ │ ├── src/ │ │ │ ├── bot/ │ │ │ │ └── bot.html │ │ │ └── extension.ts │ │ └── tsconfig.json │ ├── StackOverflowBot.sln │ └── env.template ├── _config.yml ├── blog-samples/ │ ├── CSharp/ │ │ ├── AzureSql-StateClient/ │ │ │ ├── Microsoft.Bot.Sample.AzureSql/ │ │ │ │ ├── App_Start/ │ │ │ │ │ └── WebApiConfig.cs │ │ │ │ ├── Controllers/ │ │ │ │ │ └── MessagesController.cs │ │ │ │ ├── Dialogs/ │ │ │ │ │ └── RootDialog.cs │ │ │ │ ├── Global.asax │ │ │ │ ├── Global.asax.cs │ │ │ │ ├── Microsoft.Bot.Sample.AzureSql.csproj │ │ │ │ ├── Migrations/ │ │ │ │ │ ├── 201707121827490_Initial Setup.Designer.cs │ │ │ │ │ ├── 201707121827490_Initial Setup.cs │ │ │ │ │ ├── 201707121827490_Initial Setup.resx │ │ │ │ │ └── Configuration.cs │ │ │ │ ├── Properties/ │ │ │ │ │ └── AssemblyInfo.cs │ │ │ │ ├── SqlStateService/ │ │ │ │ │ ├── SqlBotDataContext.cs │ │ │ │ │ ├── SqlBotDataEntity.cs │ │ │ │ │ └── SqlBotDataStore.cs │ │ │ │ ├── Web.Debug.config │ │ │ │ ├── Web.Release.config │ │ │ │ ├── Web.config │ │ │ │ ├── default.htm │ │ │ │ └── packages.config │ │ │ ├── Microsoft.Bot.Sample.AzureSql.sln │ │ │ └── README.md │ │ ├── Bot-Feedback-Sample/ │ │ │ ├── Bot-Feedback-Sample/ │ │ │ │ ├── App_Start/ │ │ │ │ │ └── WebApiConfig.cs │ │ │ │ ├── ApplicationInsights.config │ │ │ │ ├── Bot-Feedback-Sample.csproj │ │ │ │ ├── Connected Services/ │ │ │ │ │ └── Application Insights/ │ │ │ │ │ └── ConnectedService.json │ │ │ │ ├── Controllers/ │ │ │ │ │ └── MessagesController.cs │ │ │ │ ├── Dialogs/ │ │ │ │ │ ├── FeedbackDialog.cs │ │ │ │ │ └── QnADialog.cs │ │ │ │ ├── Global.asax │ │ │ │ ├── Global.asax.cs │ │ │ │ ├── Properties/ │ │ │ │ │ └── AssemblyInfo.cs │ │ │ │ ├── Web.Debug.config │ │ │ │ ├── Web.Release.config │ │ │ │ ├── Web.config │ │ │ │ ├── default.htm │ │ │ │ └── packages.config │ │ │ ├── Bot-Feedback-Sample.sln │ │ │ └── README.md │ │ ├── BotStateExport/ │ │ │ ├── .gitignore │ │ │ ├── BotStateExport/ │ │ │ │ ├── BotStateExport/ │ │ │ │ │ ├── App.config │ │ │ │ │ ├── BotStateExport.csproj │ │ │ │ │ ├── DocumentDbBotDataStore.cs │ │ │ │ │ ├── Extensions.cs │ │ │ │ │ ├── Program.cs │ │ │ │ │ ├── Properties/ │ │ │ │ │ │ └── AssemblyInfo.cs │ │ │ │ │ ├── TableBotDataStore.cs │ │ │ │ │ └── packages.config │ │ │ │ └── BotStateExport.sln │ │ │ └── README.md │ │ ├── Custom-State-BotBuilder-Azure-Sample/ │ │ │ ├── Azure-DocumentDB-Custom-State/ │ │ │ │ ├── App_Start/ │ │ │ │ │ └── WebApiConfig.cs │ │ │ │ ├── Azure-DocumentDB-Custom-State.csproj │ │ │ │ ├── Controllers/ │ │ │ │ │ └── MessagesController.cs │ │ │ │ ├── Dialogs/ │ │ │ │ │ └── RootDialog.cs │ │ │ │ ├── Global.asax │ │ │ │ ├── Global.asax.cs │ │ │ │ ├── Properties/ │ │ │ │ │ └── AssemblyInfo.cs │ │ │ │ ├── README.md │ │ │ │ ├── Web.Debug.config │ │ │ │ ├── Web.Release.config │ │ │ │ ├── Web.config │ │ │ │ ├── default.htm │ │ │ │ └── packages.config │ │ │ ├── Azure-Table-Custom-State/ │ │ │ │ ├── App_Start/ │ │ │ │ │ └── WebApiConfig.cs │ │ │ │ ├── Azure-Table-Custom-State.csproj │ │ │ │ ├── Controllers/ │ │ │ │ │ └── MessagesController.cs │ │ │ │ ├── Dialogs/ │ │ │ │ │ └── RootDialog.cs │ │ │ │ ├── Global.asax │ │ │ │ ├── Global.asax.cs │ │ │ │ ├── Properties/ │ │ │ │ │ └── AssemblyInfo.cs │ │ │ │ ├── README.md │ │ │ │ ├── Web.Debug.config │ │ │ │ ├── Web.Release.config │ │ │ │ ├── Web.config │ │ │ │ ├── default.htm │ │ │ │ └── packages.config │ │ │ ├── Custom-State-Sample.sln │ │ │ └── README.md │ │ ├── FacebookHandover/ │ │ │ ├── FacebookHandover.sln │ │ │ ├── FacebookModel/ │ │ │ │ ├── FacebookPassThreadControl.cs │ │ │ │ ├── FacebookPayload.cs │ │ │ │ ├── FacebookPsid.cs │ │ │ │ ├── FacebookRequestThreadControl.cs │ │ │ │ ├── FacebookStandby.cs │ │ │ │ ├── FacebookTakeThreadControl.cs │ │ │ │ └── FacebookThreadControlHelper.cs │ │ │ ├── Primary/ │ │ │ │ ├── Bots/ │ │ │ │ │ └── PrimaryBot.cs │ │ │ │ ├── Controllers/ │ │ │ │ │ └── BotController.cs │ │ │ │ ├── DeploymentTemplates/ │ │ │ │ │ ├── template-with-new-rg.json │ │ │ │ │ └── template-with-preexisting-rg.json │ │ │ │ ├── Primary.csproj │ │ │ │ ├── Program.cs │ │ │ │ ├── Properties/ │ │ │ │ │ └── launchSettings.json │ │ │ │ ├── Startup.cs │ │ │ │ ├── appsettings.Development.json │ │ │ │ ├── appsettings.json │ │ │ │ └── wwwroot/ │ │ │ │ └── default.htm │ │ │ ├── README.md │ │ │ └── Secondary/ │ │ │ ├── Bots/ │ │ │ │ └── SecondaryBot.cs │ │ │ ├── Controllers/ │ │ │ │ └── BotController.cs │ │ │ ├── DeploymentTemplates/ │ │ │ │ ├── template-with-new-rg.json │ │ │ │ └── template-with-preexisting-rg.json │ │ │ ├── Program.cs │ │ │ ├── Properties/ │ │ │ │ └── launchSettings.json │ │ │ ├── Secondary.csproj │ │ │ ├── Startup.cs │ │ │ ├── appsettings.Development.json │ │ │ ├── appsettings.json │ │ │ └── wwwroot/ │ │ │ └── default.htm │ │ ├── Luis-Scorable-QnA/ │ │ │ ├── .gitignore │ │ │ ├── Luis-Scorable-Qna/ │ │ │ │ ├── App_Start/ │ │ │ │ │ └── WebApiConfig.cs │ │ │ │ ├── Controllers/ │ │ │ │ │ └── MessagesController.cs │ │ │ │ ├── Dialogs/ │ │ │ │ │ ├── CommonResponsesDialog.cs │ │ │ │ │ ├── CommonResponsesScorable.cs │ │ │ │ │ ├── JokeDialog.cs │ │ │ │ │ ├── LuisDialog.cs │ │ │ │ │ ├── QnaDialog.cs │ │ │ │ │ └── RootDialog.cs │ │ │ │ ├── Global.asax │ │ │ │ ├── Global.asax.cs │ │ │ │ ├── Luis-Scorable-Qna-Demo.csproj │ │ │ │ ├── Properties/ │ │ │ │ │ └── AssemblyInfo.cs │ │ │ │ ├── Web.Debug.config │ │ │ │ ├── Web.Release.config │ │ │ │ ├── Web.config │ │ │ │ ├── default.htm │ │ │ │ └── packages.config │ │ │ └── Luis-Scorable-Qna.sln │ │ ├── MockChannel/ │ │ │ ├── App_Start/ │ │ │ │ └── WebApiConfig.cs │ │ │ ├── Controllers/ │ │ │ │ └── MockChannelController.cs │ │ │ ├── Global.asax │ │ │ ├── Global.asax.cs │ │ │ ├── MockChannel.csproj │ │ │ ├── MockChannel.sln │ │ │ ├── Properties/ │ │ │ │ └── AssemblyInfo.cs │ │ │ ├── README.md │ │ │ ├── Web.Debug.config │ │ │ ├── Web.Release.config │ │ │ ├── Web.config │ │ │ └── packages.config │ │ ├── Qna-Rich-Cards/ │ │ │ ├── Qna-Rich-Cards/ │ │ │ │ ├── AnswerFormats/ │ │ │ │ │ └── JsonQnaAnswer.cs │ │ │ │ ├── App_Start/ │ │ │ │ │ └── WebApiConfig.cs │ │ │ │ ├── Controllers/ │ │ │ │ │ └── MessagesController.cs │ │ │ │ ├── Dialogs/ │ │ │ │ │ ├── QnaDialog.cs │ │ │ │ │ └── RootDialog.cs │ │ │ │ ├── Global.asax │ │ │ │ ├── Global.asax.cs │ │ │ │ ├── Properties/ │ │ │ │ │ └── AssemblyInfo.cs │ │ │ │ ├── Qna-Rich-Cards.csproj │ │ │ │ ├── Web.Debug.config │ │ │ │ ├── Web.Release.config │ │ │ │ ├── Web.config │ │ │ │ ├── default.htm │ │ │ │ └── packages.config │ │ │ ├── Qna-Rich-Cards.sln │ │ │ └── README.md │ │ ├── ScorableBotSample/ │ │ │ ├── .gitattributes │ │ │ ├── .gitignore │ │ │ ├── README.md │ │ │ ├── ScorableBot/ │ │ │ │ ├── App_Start/ │ │ │ │ │ └── WebApiConfig.cs │ │ │ │ ├── Controllers/ │ │ │ │ │ └── MessagesController.cs │ │ │ │ ├── Dialogs/ │ │ │ │ │ ├── Balance/ │ │ │ │ │ │ ├── Current/ │ │ │ │ │ │ │ └── CheckBalanceCurrentDialog.cs │ │ │ │ │ │ ├── Savings/ │ │ │ │ │ │ │ └── CheckBalanceSavingsDialog.cs │ │ │ │ │ │ ├── ScorableCheckBalance.cs │ │ │ │ │ │ └── ScorableCheckBalanceDialog.cs │ │ │ │ │ ├── MakePayment/ │ │ │ │ │ │ ├── ScorableMakePayment.cs │ │ │ │ │ │ └── ScorableMakePaymentDialog.cs │ │ │ │ │ └── RootDialog.cs │ │ │ │ ├── Global.asax │ │ │ │ ├── Global.asax.cs │ │ │ │ ├── Properties/ │ │ │ │ │ └── AssemblyInfo.cs │ │ │ │ ├── ScorableBot.csproj │ │ │ │ ├── Web.Debug.config │ │ │ │ ├── Web.Release.config │ │ │ │ ├── Web.config │ │ │ │ ├── default.htm │ │ │ │ └── packages.config │ │ │ └── ScorableBotSample.sln │ │ └── TriviaBotSpeechSample/ │ │ ├── .gitignore │ │ ├── LICENSE │ │ ├── README.md │ │ ├── TriviaApp/ │ │ │ ├── App.xaml │ │ │ ├── App.xaml.cs │ │ │ ├── BotConnection.cs │ │ │ ├── Converters/ │ │ │ │ └── BoolToVisibilityConverter.cs │ │ │ ├── MainPage.xaml │ │ │ ├── MainPage.xaml.cs │ │ │ ├── Package.appxmanifest │ │ │ ├── Properties/ │ │ │ │ ├── AssemblyInfo.cs │ │ │ │ └── Default.rd.xml │ │ │ ├── TriviaApp.csproj │ │ │ ├── ViewModels/ │ │ │ │ ├── AnswerCard.cs │ │ │ │ ├── ChatCard.cs │ │ │ │ ├── CountdownTimer.cs │ │ │ │ └── ObservableDictionary.cs │ │ │ ├── packages.config │ │ │ └── project.json │ │ ├── TriviaBot/ │ │ │ ├── App_Start/ │ │ │ │ └── WebApiConfig.cs │ │ │ ├── Controllers/ │ │ │ │ └── MessagesController.cs │ │ │ ├── Global.asax │ │ │ ├── Global.asax.cs │ │ │ ├── Luis/ │ │ │ │ ├── LuisEntity.cs │ │ │ │ ├── LuisIntent.cs │ │ │ │ ├── LuisResult.cs │ │ │ │ └── QueryLuis.cs │ │ │ ├── Properties/ │ │ │ │ └── AssemblyInfo.cs │ │ │ ├── Runtime/ │ │ │ │ ├── BotState.cs │ │ │ │ ├── Categories.cs │ │ │ │ ├── EnumExtensions.cs │ │ │ │ ├── Extensions.cs │ │ │ │ ├── Responses.cs │ │ │ │ ├── SsmlWrapper.cs │ │ │ │ ├── TriviaResponse.cs │ │ │ │ └── Utility.cs │ │ │ ├── Shared/ │ │ │ │ ├── AppEntities.cs │ │ │ │ └── MessageType.cs │ │ │ ├── TriviaBot.csproj │ │ │ ├── TriviaDialog.cs │ │ │ ├── Web.Debug.config │ │ │ ├── Web.Release.config │ │ │ ├── Web.config │ │ │ ├── default.htm │ │ │ └── packages.config │ │ ├── TriviaBotLU.json │ │ └── TriviaBotSpeechSample.sln │ ├── Node/ │ │ ├── Blog-CustomState-CosmosDB/ │ │ │ ├── README.md │ │ │ ├── app.js │ │ │ └── package.json │ │ ├── Blog-Qna-Attachments/ │ │ │ ├── README.md │ │ │ ├── app.js │ │ │ └── package.json │ │ └── Blog-Redux-Bot/ │ │ ├── .gitignore │ │ ├── LICENSE │ │ ├── README.md │ │ ├── app.js │ │ ├── package.json │ │ ├── public/ │ │ │ └── index.html │ │ └── redux/ │ │ ├── conversationActions.js │ │ ├── dialogActions.js │ │ ├── loadStore.js │ │ ├── reducer.js │ │ └── sagas/ │ │ ├── default.js │ │ └── dialog.js │ └── README.md ├── docs-samples/ │ ├── CSharp/ │ │ └── Simple-LUIS-Notes-Sample/ │ │ ├── Notes.json │ │ ├── Simple-LUIS-Notes-Sample/ │ │ │ ├── App_Start/ │ │ │ │ └── WebApiConfig.cs │ │ │ ├── Controllers/ │ │ │ │ └── MessagesController.cs │ │ │ ├── Dialogs/ │ │ │ │ └── SimpleNoteDialog.cs │ │ │ ├── Global.asax │ │ │ ├── Global.asax.cs │ │ │ ├── NotesBot.csproj │ │ │ ├── Properties/ │ │ │ │ └── AssemblyInfo.cs │ │ │ ├── Web.Debug.config │ │ │ ├── Web.Release.config │ │ │ ├── Web.config │ │ │ ├── default.htm │ │ │ └── packages.config │ │ ├── Simple-LUIS-Notes-Sample.sln │ │ ├── VSIX/ │ │ │ └── readme.md │ │ └── readme.md │ ├── Node/ │ │ └── basics-naturalLanguage/ │ │ ├── Notes.json │ │ ├── basicNote-intentDialog.js │ │ ├── basicNote.js │ │ └── readme.md │ ├── README.md │ ├── V4/ │ │ └── JS/ │ │ └── contosocafebot-luis-dialogs/ │ │ ├── .gitignore │ │ ├── .vscode/ │ │ │ └── launch.json │ │ ├── cafeLUISModel.json │ │ ├── lib/ │ │ │ ├── CafeLUISModel.d.ts │ │ │ ├── CafeLUISModel.js │ │ │ ├── luisbot.d.ts │ │ │ └── luisbot.js │ │ ├── package.json │ │ ├── readme.md │ │ ├── src/ │ │ │ ├── .vscode/ │ │ │ │ └── launch.json │ │ │ ├── CafeLUISModel.ts │ │ │ └── luisbot.ts │ │ └── tsconfig.json │ ├── v3Node/ │ │ └── startNewDialog/ │ │ └── botadapter.js │ └── web-chat-speech/ │ ├── index.html │ └── readme.md └── swagger/ ├── ConnectorAPI.json └── StateAPI.json ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitattributes ================================================ ############################################################################### # Set default behavior to automatically normalize line endings. ############################################################################### * text=auto ############################################################################### # Set default behavior for command prompt diff. # # This is need for earlier builds of msysgit that does not have it on by # default for csharp files. # Note: This is only used by command line ############################################################################### #*.cs diff=csharp ############################################################################### # Set the merge driver for project and solution files # # Merging from the command prompt will add diff markers to the files if there # are conflicts (Merging from VS is not affected by the settings below, in VS # the diff markers are never inserted). Diff markers may cause the following # file extensions to fail to load in VS. An alternative would be to treat # these files as binary and thus will always conflict and require user # intervention with every merge. To do so, just uncomment the entries below ############################################################################### #*.sln merge=binary #*.csproj merge=binary #*.vbproj merge=binary #*.vcxproj merge=binary #*.vcproj merge=binary #*.dbproj merge=binary #*.fsproj merge=binary #*.lsproj merge=binary #*.wixproj merge=binary #*.modelproj merge=binary #*.sqlproj merge=binary #*.wwaproj merge=binary ############################################################################### # behavior for image files # # image files are treated as binary by default. ############################################################################### #*.jpg binary #*.png binary #*.gif binary ############################################################################### # diff behavior for common document formats # # Convert binary document formats to text before diffing them. This feature # is only available from the command line. Turn it on by uncommenting the # entries below. ############################################################################### #*.doc diff=astextplain #*.DOC diff=astextplain #*.docx diff=astextplain #*.DOCX diff=astextplain #*.dot diff=astextplain #*.DOT diff=astextplain #*.pdf diff=astextplain #*.PDF diff=astextplain #*.rtf diff=astextplain #*.RTF diff=astextplain ================================================ FILE: .gitignore ================================================ ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. # User-specific files *.suo *.user *.userosscache *.sln.docstates # User-specific files (MonoDevelop/Xamarin Studio) *.userprefs # Build results [Dd]ebug/ [Dd]ebugPublic/ [Rr]elease/ [Rr]eleases/ x64/ x86/ bld/ [Bb]in/ [Oo]bj/ [Ll]og/ # Visual Studio 2015 cache/options directory .vs/ # Uncomment if you have tasks that create the project's static files in wwwroot #wwwroot/ # MSTest test Results [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* # NUNIT *.VisualState.xml TestResult.xml # Build Results of an ATL Project [Dd]ebugPS/ [Rr]eleasePS/ dlldata.c # DNX project.lock.json artifacts/ *_i.c *_p.c *_i.h *.ilk *.meta *.obj *.pch *.pdb *.pgc *.pgd *.rsp *.sbr *.tlb *.tli *.tlh *.tmp *.tmp_proj *.log *.vspscc *.vssscc .builds *.pidb *.svclog *.scc # Chutzpah Test files _Chutzpah* # Visual C++ cache files ipch/ *.aps *.ncb *.opendb *.opensdf *.sdf *.cachefile *.VC.db *.VC.VC.opendb # Visual Studio profiler *.psess *.vsp *.vspx *.sap # TFS 2012 Local Workspace $tf/ # Guidance Automation Toolkit *.gpState # ReSharper is a .NET coding add-in _ReSharper*/ *.[Rr]e[Ss]harper *.DotSettings.user # JustCode is a .NET coding add-in .JustCode # TeamCity is a build add-in _TeamCity* # DotCover is a Code Coverage Tool *.dotCover # NCrunch _NCrunch_* .*crunch*.local.xml nCrunchTemp_* # MightyMoose *.mm.* AutoTest.Net/ # Web workbench (sass) .sass-cache/ # Installshield output folder [Ee]xpress/ # DocProject is a documentation generator add-in DocProject/buildhelp/ DocProject/Help/*.HxT DocProject/Help/*.HxC DocProject/Help/*.hhc DocProject/Help/*.hhk DocProject/Help/*.hhp DocProject/Help/Html2 DocProject/Help/html # Click-Once directory publish/ # Publish Web Output *.[Pp]ublish.xml *.azurePubxml # TODO: Comment the next line if you want to checkin your web deploy settings # but database connection strings (with potential passwords) will be unencrypted *.pubxml *.publishproj # Microsoft Azure Web App publish settings. Comment the next line if you want to # checkin your Azure Web App publish settings, but sensitive information contained # in these scripts will be unencrypted PublishScripts/ # NuGet Packages *.nupkg # The packages folder can be ignored because of Package Restore **/packages/* # except build/, which is used as an MSBuild target. !**/packages/build/ # Uncomment if necessary however generally it will be regenerated when needed #!**/packages/repositories.config # NuGet v3's project.json files produces more ignoreable files *.nuget.props *.nuget.targets # Microsoft Azure Build Output csx/ *.build.csdef # Microsoft Azure Emulator ecf/ rcf/ # Windows Store app package directories and files AppPackages/ BundleArtifacts/ Package.StoreAssociation.xml _pkginfo.txt # Visual Studio cache files # files ending in .cache can be ignored *.[Cc]ache # but keep track of directories ending in .cache !*.[Cc]ache/ # Others ClientBin/ ~$* *~ *.dbmdl *.dbproj.schemaview *.pfx *.publishsettings node_modules/ orleans.codegen.cs # Since there are multiple workflows, uncomment next line to ignore bower_components # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) #bower_components/ # RIA/Silverlight projects Generated_Code/ # Backup & report files from converting an old project file # to a newer Visual Studio version. Backup files are not needed, # because we have git ;-) _UpgradeReport_Files/ Backup*/ UpgradeLog*.XML UpgradeLog*.htm # SQL Server files *.mdf *.ldf # Business Intelligence projects *.rdl.data *.bim.layout *.bim_*.settings # Microsoft Fakes FakesAssemblies/ # GhostDoc plugin setting file *.GhostDoc.xml # Node.js Tools for Visual Studio .ntvs_analysis.dat # Visual Studio 6 build log *.plg # Visual Studio 6 workspace options file *.opt # Visual Studio LightSwitch build output **/*.HTMLClient/GeneratedArtifacts **/*.DesktopClient/GeneratedArtifacts **/*.DesktopClient/ModelManifest.xml **/*.Server/GeneratedArtifacts **/*.Server/ModelManifest.xml _Pvt_Extensions # Paket dependency manager .paket/paket.exe paket-files/ # FAKE - F# Make .fake/ # JetBrains Rider .idea/ *.sln.iml # Misc package-lock.json ================================================ FILE: LICENSE ================================================ 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 ================================================ FILE: README.md ================================================ # Bot Framework Samples This repo contains samples that are specifically used in the blog posts. You can find additional [SDK V4](https://github.com/Microsoft/BotBuilder-Samples/tree/master/samples) and [SDK V3](https://github.com/Microsoft/BotBuilder-Samples/tree/v3-sdk-samples) in the BotBuilder-Samples repo. ## Contributing This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. ================================================ FILE: SDKV4-Samples/.gitattributes ================================================ ############################################################################### # Set default behavior to automatically normalize line endings. ############################################################################### * text=auto ############################################################################### # Set default behavior for command prompt diff. # # This is need for earlier builds of msysgit that does not have it on by # default for csharp files. # Note: This is only used by command line ############################################################################### #*.cs diff=csharp ############################################################################### # Set the merge driver for project and solution files # # Merging from the command prompt will add diff markers to the files if there # are conflicts (Merging from VS is not affected by the settings below, in VS # the diff markers are never inserted). Diff markers may cause the following # file extensions to fail to load in VS. An alternative would be to treat # these files as binary and thus will always conflict and require user # intervention with every merge. To do so, just uncomment the entries below ############################################################################### #*.sln merge=binary #*.csproj merge=binary #*.vbproj merge=binary #*.vcxproj merge=binary #*.vcproj merge=binary #*.dbproj merge=binary #*.fsproj merge=binary #*.lsproj merge=binary #*.wixproj merge=binary #*.modelproj merge=binary #*.sqlproj merge=binary #*.wwaproj merge=binary ############################################################################### # behavior for image files # # image files are treated as binary by default. ############################################################################### #*.jpg binary #*.png binary #*.gif binary ############################################################################### # diff behavior for common document formats # # Convert binary document formats to text before diffing them. This feature # is only available from the command line. Turn it on by uncommenting the # entries below. ############################################################################### #*.doc diff=astextplain #*.DOC diff=astextplain #*.docx diff=astextplain #*.DOCX diff=astextplain #*.dot diff=astextplain #*.DOT diff=astextplain #*.pdf diff=astextplain #*.PDF diff=astextplain #*.rtf diff=astextplain #*.RTF diff=astextplain ================================================ FILE: SDKV4-Samples/.gitignore ================================================ ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. # User-specific files *.suo *.user *.userosscache *.sln.docstates # User-specific files (MonoDevelop/Xamarin Studio) *.userprefs # Build results [Dd]ebug/ [Dd]ebugPublic/ [Rr]elease/ [Rr]eleases/ x64/ x86/ bld/ [Bb]in/ [Oo]bj/ [Ll]og/ # Visual Studio 2015 cache/options directory .vs/ # Uncomment if you have tasks that create the project's static files in wwwroot #wwwroot/ # MSTest test Results [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* # NUNIT *.VisualState.xml TestResult.xml # Build Results of an ATL Project [Dd]ebugPS/ [Rr]eleasePS/ dlldata.c # DNX project.lock.json artifacts/ *_i.c *_p.c *_i.h *.ilk *.meta *.obj *.pch *.pdb *.pgc *.pgd *.rsp *.sbr *.tlb *.tli *.tlh *.tmp *.tmp_proj *.log *.vspscc *.vssscc .builds *.pidb *.svclog *.scc # Chutzpah Test files _Chutzpah* # Visual C++ cache files ipch/ *.aps *.ncb *.opendb *.opensdf *.sdf *.cachefile *.VC.db *.VC.VC.opendb # Visual Studio profiler *.psess *.vsp *.vspx *.sap # TFS 2012 Local Workspace $tf/ # Guidance Automation Toolkit *.gpState # ReSharper is a .NET coding add-in _ReSharper*/ *.[Rr]e[Ss]harper *.DotSettings.user # JustCode is a .NET coding add-in .JustCode # TeamCity is a build add-in _TeamCity* # DotCover is a Code Coverage Tool *.dotCover # NCrunch _NCrunch_* .*crunch*.local.xml nCrunchTemp_* # MightyMoose *.mm.* AutoTest.Net/ # Web workbench (sass) .sass-cache/ # Installshield output folder [Ee]xpress/ # DocProject is a documentation generator add-in DocProject/buildhelp/ DocProject/Help/*.HxT DocProject/Help/*.HxC DocProject/Help/*.hhc DocProject/Help/*.hhk DocProject/Help/*.hhp DocProject/Help/Html2 DocProject/Help/html # Click-Once directory publish/ # Publish Web Output *.[Pp]ublish.xml *.azurePubxml # TODO: Comment the next line if you want to checkin your web deploy settings # but database connection strings (with potential passwords) will be unencrypted *.pubxml *.publishproj # Microsoft Azure Web App publish settings. Comment the next line if you want to # checkin your Azure Web App publish settings, but sensitive information contained # in these scripts will be unencrypted PublishScripts/ # NuGet Packages *.nupkg # The packages folder can be ignored because of Package Restore **/packages/* # except build/, which is used as an MSBuild target. !**/packages/build/ # Uncomment if necessary however generally it will be regenerated when needed #!**/packages/repositories.config # NuGet v3's project.json files produces more ignoreable files *.nuget.props *.nuget.targets # Microsoft Azure Build Output csx/ *.build.csdef # Microsoft Azure Emulator ecf/ rcf/ # Windows Store app package directories and files AppPackages/ BundleArtifacts/ Package.StoreAssociation.xml _pkginfo.txt # Visual Studio cache files # files ending in .cache can be ignored *.[Cc]ache # but keep track of directories ending in .cache !*.[Cc]ache/ # Others ClientBin/ ~$* *~ *.dbmdl *.dbproj.schemaview *.pfx *.publishsettings node_modules/ orleans.codegen.cs # Since there are multiple workflows, uncomment next line to ignore bower_components # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) #bower_components/ # RIA/Silverlight projects Generated_Code/ # Backup & report files from converting an old project file # to a newer Visual Studio version. Backup files are not needed, # because we have git ;-) _UpgradeReport_Files/ Backup*/ UpgradeLog*.XML UpgradeLog*.htm # SQL Server files *.mdf *.ldf # Business Intelligence projects *.rdl.data *.bim.layout *.bim_*.settings # Microsoft Fakes FakesAssemblies/ # GhostDoc plugin setting file *.GhostDoc.xml # Node.js Tools for Visual Studio .ntvs_analysis.dat # Visual Studio 6 build log *.plg # Visual Studio 6 workspace options file *.opt # Visual Studio LightSwitch build output **/*.HTMLClient/GeneratedArtifacts **/*.DesktopClient/GeneratedArtifacts **/*.DesktopClient/ModelManifest.xml **/*.Server/GeneratedArtifacts **/*.Server/ModelManifest.xml _Pvt_Extensions # Paket dependency manager .paket/paket.exe paket-files/ # FAKE - F# Make .fake/ # JetBrains Rider .idea/ *.sln.iml ================================================ FILE: SDKV4-Samples/LICENSE ================================================ 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 ================================================ FILE: SDKV4-Samples/README.md ================================================ # Bot Framework Samples This branch is for Bot Builder SDK V4 samples used in the documentation. ## Contributing This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. ================================================ FILE: SDKV4-Samples/_config.yml ================================================ theme: jekyll-theme-cayman ================================================ FILE: SDKV4-Samples/dotnet_core/ComplexDialogBot/BotBuilder.ruleset ================================================  ================================================ FILE: SDKV4-Samples/dotnet_core/ComplexDialogBot/ComplexDialogBot.cs ================================================ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. namespace Microsoft.BotBuilderSamples { using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.Bot.Builder; using Microsoft.Bot.Builder.Dialogs; using Microsoft.Bot.Builder.Dialogs.Choices; using Microsoft.Bot.Schema; /// /// Represents a bot that processes incoming activities. /// For each user interaction, an instance of this class is created and the OnTurnAsync method is called. /// This is a Transient lifetime service. Transient lifetime services are created /// each time they're requested. For each Activity received, a new instance of this /// class is created. Objects that are expensive to construct, or have a lifetime /// beyond the single turn, should be carefully managed. /// For example, the object and associated /// object are created with a singleton lifetime. /// /// public class ComplexDialogBot : IBot { private const string WelcomeText = "Welcome to ComplexDialogBot. This bot provides a complex conversation, with multiple dialogs. " + "Type anything to get started."; // Define the dialog and prompt names for the bot. private const string TopLevelDialog = "dialog-topLevel"; private const string ReviewSelectionDialog = "dialog-reviewSeleciton"; private const string NamePrompt = "prompt-name"; private const string AgePrompt = "prompt-age"; private const string SelectionPrompt = "prompt-companySlection"; // Define a "done" response for the company selection prompt. private const string DoneOption = "done"; // Define value names for values tracked inside the dialogs. private const string UserInfo = "value-userInfo"; private const string CompaniesSelected = "value-companiesSelected"; // Define the company choices for the company selection prompt. private readonly string[] _companyOptions = new string[] { "Adatum Corporation", "Contoso Suites", "Graphic Design Institute", "Wide World Importers", }; private readonly ComplexDialogBotAccessors _accessors; /// /// The that contains all the Dialogs that can be used at runtime. /// private readonly DialogSet _dialogs; /// /// Initializes a new instance of the class. /// /// A class containing used to manage state. public ComplexDialogBot(ComplexDialogBotAccessors accessors) { _accessors = accessors ?? throw new ArgumentNullException(nameof(accessors)); // Create a dialog set for the bot. It requires a DialogState accessor, with which // to retrieve the dialog state from the turn context. _dialogs = new DialogSet(accessors.DialogStateAccessor); // Add the prompts we need to the dialog set. _dialogs .Add(new TextPrompt(NamePrompt)) .Add(new NumberPrompt(AgePrompt)) .Add(new ChoicePrompt(SelectionPrompt)); // Add the dialogs we need to the dialog set. _dialogs.Add(new WaterfallDialog(TopLevelDialog) .AddStep(NameStepAsync) .AddStep(AgeStepAsync) .AddStep(StartSelectionStepAsync) .AddStep(AcknowledgementStepAsync)); _dialogs.Add(new WaterfallDialog(ReviewSelectionDialog) .AddStep(SelectionStepAsync) .AddStep(LoopStepAsync)); } /// /// Every conversation turn for our EchoBot will call this method. /// /// A containing all the data needed /// for processing this conversation turn. /// (Optional) A that can be used by other objects /// or threads to receive notice of cancellation. /// A that represents the work queued to execute. /// /// /// public async Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken = default(CancellationToken)) { if (turnContext == null) { throw new ArgumentNullException(nameof(turnContext)); } // Handle Message activity type, which is the main activity type for shown within a conversational interface // Message activities may contain text, speech, interactive cards, and binary or unknown attachments. // see https://aka.ms/about-bot-activity-message to learn more about the message and other activity types if (turnContext.Activity.Type == ActivityTypes.Message) { // Run the DialogSet - let the framework identify the current state of the dialog from // the dialog stack and figure out what (if any) is the active dialog. DialogContext dialogContext = await _dialogs.CreateContextAsync(turnContext, cancellationToken); DialogTurnResult results = await dialogContext.ContinueDialogAsync(cancellationToken); switch (results.Status) { case DialogTurnStatus.Cancelled: case DialogTurnStatus.Empty: // If there is no active dialog, we should clear the user info and start a new dialog. await _accessors.UserProfileAccessor.SetAsync(turnContext, new UserProfile(), cancellationToken); await _accessors.UserState.SaveChangesAsync(turnContext, false, cancellationToken); await dialogContext.BeginDialogAsync(TopLevelDialog, null, cancellationToken); break; case DialogTurnStatus.Complete: // If we just finished the dialog, capture and display the results. UserProfile userInfo = results.Result as UserProfile; string status = "You are signed up to review " + (userInfo.CompaniesToReview.Count is 0 ? "no companies" : string.Join(" and ", userInfo.CompaniesToReview)) + "."; await turnContext.SendActivityAsync(status); await _accessors.UserProfileAccessor.SetAsync(turnContext, userInfo, cancellationToken); await _accessors.UserState.SaveChangesAsync(turnContext, false, cancellationToken); break; case DialogTurnStatus.Waiting: // If there is an active dialog, we don't need to do anything here. break; } await _accessors.ConversationState.SaveChangesAsync(turnContext, false, cancellationToken); } // Processes ConversationUpdate Activities to welcome the user. else if (turnContext.Activity.Type == ActivityTypes.ConversationUpdate) { if (turnContext.Activity.MembersAdded != null) { await SendWelcomeMessageAsync(turnContext, cancellationToken); } } else { await turnContext.SendActivityAsync($"{turnContext.Activity.Type} event detected", cancellationToken: cancellationToken); } } /// /// Sends a welcome message to the user. /// /// A containing all the data needed /// for processing this conversation turn. /// (Optional) A that can be used by other objects /// or threads to receive notice of cancellation. /// A that represents the work queued to execute. private static async Task SendWelcomeMessageAsync(ITurnContext turnContext, CancellationToken cancellationToken) { foreach (ChannelAccount member in turnContext.Activity.MembersAdded) { if (member.Id != turnContext.Activity.Recipient.Id) { Activity reply = turnContext.Activity.CreateReply(); reply.Text = WelcomeText; await turnContext.SendActivityAsync(reply, cancellationToken); } } } /// The first step of the top-level dialog. /// The waterfall step context for the current turn. /// A cancellation token that can be used by other objects /// or threads to receive notice of cancellation. /// A task that represents the work queued to execute. /// If the task is successful, the result contains a to /// communicate some flow control back to the containing WaterfallDialog. private static async Task NameStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken) { // Create an object in which to collect the user's information within the dialog. stepContext.Values[UserInfo] = new UserProfile(); // Ask the user to enter their name. return await stepContext.PromptAsync( NamePrompt, new PromptOptions { Prompt = MessageFactory.Text("Please enter your name.") }, cancellationToken); } /// The second step of the top-level dialog. /// The waterfall step context for the current turn. /// A cancellation token that can be used by other objects /// or threads to receive notice of cancellation. /// A task that represents the work queued to execute. /// If the task is successful, the result contains a to /// communicate some flow control back to the containing WaterfallDialog. private async Task AgeStepAsync( WaterfallStepContext stepContext, CancellationToken cancellationToken) { // Set the user's name to what they entered in response to the name prompt. ((UserProfile)stepContext.Values[UserInfo]).Name = (string)stepContext.Result; // Ask the user to enter their age. return await stepContext.PromptAsync( AgePrompt, new PromptOptions { Prompt = MessageFactory.Text("Please enter your age.") }, cancellationToken); } /// The third step of the top-level dialog. /// The waterfall step context for the current turn. /// A cancellation token that can be used by other objects /// or threads to receive notice of cancellation. /// A task that represents the work queued to execute. /// If the task is successful, the result contains a to /// communicate some flow control back to the containing WaterfallDialog. private async Task StartSelectionStepAsync( WaterfallStepContext stepContext, CancellationToken cancellationToken) { // Set the user's age to what they entered in response to the age prompt. int age = (int)stepContext.Result; ((UserProfile)stepContext.Values[UserInfo]).Age = age; if (age < 25) { // If they are too young, skip the review selection dialog, and pass an empty list to the next step. await stepContext.Context.SendActivityAsync( MessageFactory.Text("You must be 25 or older to participate."), cancellationToken); return await stepContext.NextAsync(new List(), cancellationToken); } else { // Otherwise, start the review selection dialog. return await stepContext.BeginDialogAsync(ReviewSelectionDialog, null, cancellationToken); } } /// The final step of the top-level dialog. /// The waterfall step context for the current turn. /// A cancellation token that can be used by other objects /// or threads to receive notice of cancellation. /// A task that represents the work queued to execute. /// If the task is successful, the result contains a to /// communicate some flow control back to the containing WaterfallDialog. private async Task AcknowledgementStepAsync( WaterfallStepContext stepContext, CancellationToken cancellationToken) { // Set the user's company selection to what they entered in the review-selection dialog. List list = stepContext.Result as List; ((UserProfile)stepContext.Values[UserInfo]).CompaniesToReview = list ?? new List(); // Thank them for participating. await stepContext.Context.SendActivityAsync( MessageFactory.Text($"Thanks for participating, {((UserProfile)stepContext.Values[UserInfo]).Name}."), cancellationToken); // Exit the dialog, returning the collected user information. return await stepContext.EndDialogAsync(stepContext.Values[UserInfo], cancellationToken); } /// The first step of the review-selection dialog. /// The waterfall step context for the current turn. /// A cancellation token that can be used by other objects /// or threads to receive notice of cancellation. /// A task that represents the work queued to execute. /// If the task is successful, the result contains a to /// communicate some flow control back to the containing WaterfallDialog. private async Task SelectionStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken) { // Continue using the same selection list, if any, from the previous iteration of this dialog. List list = stepContext.Options as List ?? new List(); stepContext.Values[CompaniesSelected] = list; // Create a prompt message. string message; if (list.Count is 0) { message = $"Please choose a company to review, or `{DoneOption}` to finish."; } else { message = $"You have selected **{list[0]}**. You can review an additional company, " + $"or choose `{DoneOption}` to finish."; } // Create the list of options to choose from. List options = _companyOptions.ToList(); options.Add(DoneOption); if (list.Count > 0) { options.Remove(list[0]); } // Prompt the user for a choice. return await stepContext.PromptAsync( SelectionPrompt, new PromptOptions { Prompt = MessageFactory.Text(message), RetryPrompt = MessageFactory.Text("Please choose an option from the list."), Choices = ChoiceFactory.ToChoices(options), }, cancellationToken); } /// The final step of the review-selection dialog. /// The waterfall step context for the current turn. /// A cancellation token that can be used by other objects /// or threads to receive notice of cancellation. /// A task that represents the work queued to execute. /// If the task is successful, the result contains a to /// communicate some flow control back to the containing WaterfallDialog. private async Task LoopStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken) { // Retrieve their selection list, the choice they made, and whether they chose to finish. List list = stepContext.Values[CompaniesSelected] as List; FoundChoice choice = (FoundChoice)stepContext.Result; bool done = choice.Value == DoneOption; if (!done) { // If they chose a company, add it to the list. list.Add(choice.Value); } if (done || list.Count is 2) { // If they're done, exit and return their list. return await stepContext.EndDialogAsync(list, cancellationToken); } else { // Otherwise, repeat this dialog, passing in the list from this iteration. return await stepContext.ReplaceDialogAsync(ReviewSelectionDialog, list, cancellationToken); } } } } ================================================ FILE: SDKV4-Samples/dotnet_core/ComplexDialogBot/ComplexDialogBot.csproj ================================================  netcoreapp2.0 BotBuilder.ruleset Always ================================================ FILE: SDKV4-Samples/dotnet_core/ComplexDialogBot/ComplexDialogBotAccessors.cs ================================================ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. using System; using Microsoft.Bot.Builder; using Microsoft.Bot.Builder.Dialogs; namespace Microsoft.BotBuilderSamples { /// /// This class is created as a Singleton and passed into the IBot-derived constructor. /// - See constructor for how that is injected. /// - See the Startup.cs file for more details on creating the Singleton that gets /// injected into the constructor. /// public class ComplexDialogBotAccessors { /// /// Initializes a new instance of the class. /// Contains the and associated . /// /// The state object that stores the dialog state. /// The state object that stores the user state. public ComplexDialogBotAccessors(ConversationState conversationState, UserState userState) { ConversationState = conversationState ?? throw new ArgumentNullException(nameof(conversationState)); UserState = userState ?? throw new ArgumentNullException(nameof(userState)); } /// /// Gets or sets the for ConversationDialogState. /// /// /// The accessor stores the dialog state for the conversation. /// public IStatePropertyAccessor DialogStateAccessor { get; set; } /// /// Gets or sets the for CounterState. /// /// /// The accessor stores user data. /// public IStatePropertyAccessor UserProfileAccessor { get; set; } /// /// Gets the object for the conversation. /// /// The object. public ConversationState ConversationState { get; } /// /// Gets the object for the conversation. /// /// The object. public UserState UserState { get; } } } ================================================ FILE: SDKV4-Samples/dotnet_core/ComplexDialogBot/DeploymentScripts/MsbotClone/bot.recipe ================================================ { "version": "1.0", "resources": [ { "type": "endpoint", "id": "24", "name": "Sample", "url": "http://localhost:3978/api/messages" }, { "type": "endpoint", "id": "2", "name": "production", "url": "https://your-bot-url.azurewebsites.net/api/messages" }, { "type": "abs", "id": "3", "name": "complex-dialog-Bot" }, { "type": "appInsights", "id": "4", "name": "complex-dialog-Insights" }, { "type": "blob", "id": "5", "name": "complex-dialog-Blob", "container": "botstatestore" } ] } ================================================ FILE: SDKV4-Samples/dotnet_core/ComplexDialogBot/Program.cs ================================================ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. using Microsoft.AspNetCore; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Logging; namespace Microsoft.BotBuilderSamples { public class Program { public static void Main(string[] args) { BuildWebHost(args).Run(); } public static IWebHost BuildWebHost(string[] args) => WebHost.CreateDefaultBuilder(args) .ConfigureLogging((hostingContext, logging) => { // Add Azure Logging logging.AddAzureWebAppDiagnostics(); // Logging Options. // There are other logging options available: // https://docs.microsoft.com/en-us/aspnet/core/fundamentals/logging/?view=aspnetcore-2.1 // logging.AddDebug(); // logging.AddConsole(); }) // Logging Options. // Consider using Application Insights for your logging and metrics needs. // https://azure.microsoft.com/en-us/services/application-insights/ // .UseApplicationInsights() .UseStartup() .Build(); } } ================================================ FILE: SDKV4-Samples/dotnet_core/ComplexDialogBot/Properties/launchSettings.json ================================================ { "iisSettings": { "windowsAuthentication": false, "anonymousAuthentication": true, "iisExpress": { "applicationUrl": "http://localhost:3978/", "sslPort": 0 } }, "profiles": { "IIS Express": { "commandName": "IISExpress", "launchBrowser": true, "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } }, "_5.MultiTurn_Prompts_Bot": { "commandName": "Project", "launchBrowser": true, "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" }, "applicationUrl": "http://localhost:3978/" } } } ================================================ FILE: SDKV4-Samples/dotnet_core/ComplexDialogBot/README.md ================================================ This sample creates a complex conversation with dialogs and ASP.Net Core 2. # To try this sample - Clone the samples repository ```bash git clone https://github.com/Microsoft/BotFramework-Samples.git ``` - [Optional] Update the `appsettings.json` file under `BotFramework-Samples/SDKV4-Samples/dotnet_core/ComplexDialogBot/` with your botFileSecret. For Azure Bot Service bots, you can find the botFileSecret under application settings. # Running Locally ## Visual Studio - Navigate to the samples folder (`BotFramework-Samples/SDKV4-Samples/dotnet_core/ComplexDialogBot/`) and open ComplexDialogBot.csproj in Visual Studio. - Run the project (press `F5` key). ## .NET Core CLI - Install the [.NET Core CLI tools](https://docs.microsoft.com/dotnet/core/tools/?tabs=netcore2x). - Using the command line, navigate to `BotFramework-Samples/SDKV4-Samples/dotnet_core/ComplexDialogBot/` folder. - Type `dotnet run`. ## Testing the bot using Bot Framework Emulator [Microsoft Bot Framework Emulator](https://github.com/microsoft/botframework-emulator) is a desktop application that allows bot developers to test and debug their bots on localhost or running remotely through a tunnel. - Install the Bot Framework emulator from [here](https://aka.ms/botframeworkemulator). ## Connect to bot using Bot Framework Emulator **V4** - Launch the Bot Framework Emulator. - File -> Open bot and navigate to `BotFramework-Samples/SDKV4-Samples/dotnet_core/ComplexDialogBot` folder. - Select `complex-dialog.bot` file. # Further reading - [Azure Bot Service](https://docs.microsoft.com/azure/bot-service/bot-service-overview-introduction?view=azure-bot-service-4.0) - [Bot Storage](https://docs.microsoft.com/azure/bot-service/dotnet/bot-builder-dotnet-state?view=azure-bot-service-3.0&viewFallbackFrom=azure-bot-service-4.0) ================================================ FILE: SDKV4-Samples/dotnet_core/ComplexDialogBot/Startup.cs ================================================ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. namespace Microsoft.BotBuilderSamples { using System; using System.Linq; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Bot.Builder; using Microsoft.Bot.Builder.Dialogs; using Microsoft.Bot.Builder.Integration.AspNet.Core; using Microsoft.Bot.Configuration; using Microsoft.Bot.Connector.Authentication; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; /// /// The Startup class configures services and the app's request pipeline. /// public class Startup { private ILoggerFactory _loggerFactory; private bool _isProduction = false; public Startup(IHostingEnvironment env) { _isProduction = env.IsProduction(); var builder = new ConfigurationBuilder() .SetBasePath(env.ContentRootPath) .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true) .AddEnvironmentVariables(); Configuration = builder.Build(); } /// /// Gets the configuration that represents a set of key/value application configuration properties. /// /// /// The that represents a set of key/value application configuration properties. /// public IConfiguration Configuration { get; } /// /// This method gets called by the runtime. Use this method to add services to the container. /// /// Specifies the contract for a of service descriptors. /// /// /// public void ConfigureServices(IServiceCollection services) { services.AddBot(options => { var secretKey = Configuration.GetSection("botFileSecret")?.Value; var botFilePath = Configuration.GetSection("botFilePath")?.Value; // Loads .bot configuration file and adds a singleton that your Bot can access through dependency injection. var botConfig = BotConfiguration.Load(botFilePath ?? @".\complex-dialog.bot", secretKey); services.AddSingleton(sp => botConfig ?? throw new InvalidOperationException($"The .bot configuration file could not be loaded. ({botConfig})")); // Retrieve current endpoint. var environment = _isProduction ? "production" : "development"; var service = botConfig.Services.FirstOrDefault(s => s.Type == "endpoint" && s.Name == environment); if (!(service is EndpointService endpointService)) { throw new InvalidOperationException($"The .bot file does not contain an endpoint with name '{environment}'."); } options.CredentialProvider = new SimpleCredentialProvider(endpointService.AppId, endpointService.AppPassword); // Creates a logger for the application to use. ILogger logger = _loggerFactory.CreateLogger(); // Catches any errors that occur during a conversation turn and logs them. options.OnTurnError = async (context, exception) => { logger.LogError($"Exception caught : {exception}"); await context.SendActivityAsync("Sorry, it looks like something went wrong."); }; }); // Create conversation and user state management objects, using memory storage. IStorage dataStore = new MemoryStorage(); var conversationState = new ConversationState(dataStore); var userState = new UserState(dataStore); // Create and register state accessors. // Accessors created here are passed into the IBot-derived class on every turn. services.AddSingleton(sp => { // Create the custom state accessor. // State accessors enable other components to read and write individual properties of state. var accessors = new ComplexDialogBotAccessors(conversationState, userState) { DialogStateAccessor = conversationState.CreateProperty("DialogState"), UserProfileAccessor = userState.CreateProperty("UserProfile"), }; return accessors; }); } public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { _loggerFactory = loggerFactory; app.UseDefaultFiles() .UseStaticFiles() .UseBotFramework(); } } } ================================================ FILE: SDKV4-Samples/dotnet_core/ComplexDialogBot/UserProfile.cs ================================================ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. namespace Microsoft.BotBuilderSamples { using System.Collections.Generic; /// Contains information about a user. public class UserProfile { /// Gets or sets the user's name. /// The user's name. public string Name { get; set; } /// Gets or sets the user's age. /// The user's age. public int Age { get; set; } /// Gets or sets the list of companies the user wants to review. /// The list of companies the user wants to review. public List CompaniesToReview { get; set; } = new List(); } } ================================================ FILE: SDKV4-Samples/dotnet_core/ComplexDialogBot/appsettings.json ================================================ { "botFilePath": "complex-dialog.bot", "botFileSecret": "" } ================================================ FILE: SDKV4-Samples/dotnet_core/ComplexDialogBot/complex-dialog.bot ================================================ { "name": "complex-dialog-bot", "services": [ { "type": "endpoint", "name": "development", "endpoint": "http://localhost:3978/api/messages", "appId": "", "appPassword": "", "id": "24" } ], "padlock": "", "version": "2.0" } ================================================ FILE: SDKV4-Samples/dotnet_core/ComplexDialogBot/wwwroot/default.htm ================================================  Complex dialog sample
Complex Dialog Bot
Your bot is ready!
You can test your bot in the Bot Framework Emulator
by opening the .bot file in the project folder.
Visit Azure Bot Service to register your bot and add it to
various channels. The bot's endpoint URL typically looks like this:
https://your_bots_hostname/api/messages
================================================ FILE: SDKV4-Samples/dotnet_core/DialogInterruptionsBot/BotBuilder.ruleset ================================================  ================================================ FILE: SDKV4-Samples/dotnet_core/DialogInterruptionsBot/DeploymentScripts/MsbotClone/bot.recipe ================================================ { "version": "1.0", "resources": [ { "type": "endpoint", "id": "24", "name": "Sample", "url": "http://localhost:3978/api/messages" }, { "type": "endpoint", "id": "2", "name": "production", "url": "https://your-bot-url.azurewebsites.net/api/messages" }, { "type": "abs", "id": "3", "name": "complex-dialog-Bot" }, { "type": "appInsights", "id": "4", "name": "complex-dialog-Insights" }, { "type": "blob", "id": "5", "name": "complex-dialog-Blob", "container": "botstatestore" } ] } ================================================ FILE: SDKV4-Samples/dotnet_core/DialogInterruptionsBot/DialogInterruptionsBot.cs ================================================ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. namespace Microsoft.BotBuilderSamples { using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.Bot.Builder; using Microsoft.Bot.Builder.Dialogs; using Microsoft.Bot.Builder.Dialogs.Choices; using Microsoft.Bot.Schema; /// /// Represents a bot that processes incoming activities. /// For each user interaction, an instance of this class is created and the OnTurnAsync method is called. /// This is a Transient lifetime service. Transient lifetime services are created /// each time they're requested. For each Activity received, a new instance of this /// class is created. Objects that are expensive to construct, or have a lifetime /// beyond the single turn, should be carefully managed. /// For example, the object and associated /// object are created with a singleton lifetime. /// /// public class DialogInterruptionsBot : IBot { // Define the company choices for the company selection prompt. private static class Companies { public const string Adatum = "Adatum Corporation"; public const string Contoso = "Contoso Suites"; public const string Gdi = "Graphic Design Institute"; public const string Wwi = "Wide World Importers"; public static readonly IReadOnlyDictionary Options = new Dictionary { { Adatum, "A market research company" }, { Contoso, "A chain of hotels and inns" }, { Gdi, "A school for graphic design" }, { Wwi, "An importer of consumer goods" }, }; public static string MoreInfo { get; } = string.Join("\r\r", Options.Select(e => $"**{e.Key}**--{e.Value}")); } // Define interruptions for the conversation. private static class Interruptions { // "Global" interruptions. public const string Wait = "wait"; public const string Continue = "continue"; public const string Cancel = "cancel"; // "Local" interruptions (to the review selection process). public const string Finish = "finish"; public const string MoreInfo = "more info"; public const string Help = "help"; public static readonly string[] LocalOptions = new string[] { Finish, MoreInfo, Help, }; public static readonly IReadOnlyDictionary Options = new Dictionary { { Cancel, "Cancel the review sign-up process." }, { Continue, "Continues the conversation on hold, if any." }, { Finish, "Complete the review sign-up process with the currently selected companies." }, { Help, "List the available commands." }, { MoreInfo, "Display information about the companies." }, { Wait, "Puts the current conversation on hold." }, }; /// Describes the available commands. public static string HelpText { get; } = string.Join("\r\r", Options.Select(e => $"**{e.Key}**--{e.Value}")); } private const string WelcomeText = "Welcome to DialogInterruptionsBot." + " This bot provides a complex conversation, supporting various kinds of interruptions." + " Type anything to get started."; private static string GlobalHelpText { get; } = "This bot helps you sign up to review companies." + $" To pause the conversation at any time, enter `{Interruptions.Wait}`." + $" To resume the conversation, enter `{Interruptions.Continue}`."; private const string CancellationText = "We have cancelled your sign up. Thankyou."; private static string OnHoldText { get; } = "The conversation is on hold." + $" Enter `{Interruptions.Continue}` to continue the conversation where you left off."; // Define the dialog and prompt names for the bot. private const string TopLevelDialog = "dialog-topLevel"; private const string ReviewSelectionDialog = "dialog-reviewSeleciton"; private const string OnHoldDialog = "dialog-onHold"; private const string NamePrompt = "prompt-name"; private const string AgePrompt = "prompt-age"; private const string SelectionPrompt = "prompt-companySlection"; // Define value names for values tracked inside the dialogs. private const string UserInfo = "value-userInfo"; private const string CompaniesSelected = "value-companiesSelected"; /// /// Contains the state property accessors and state management objects for the bot. /// private readonly DialogInterruptionsBotAccessors _accessors; /// /// The that contains all the Dialogs that can be used at runtime. /// private readonly DialogSet _dialogs; /// /// Initializes a new instance of the class. /// /// A class containing used to manage state. public DialogInterruptionsBot(DialogInterruptionsBotAccessors accessors) { _accessors = accessors ?? throw new ArgumentNullException(nameof(accessors)); // Create a dialog set for the bot. It requires a DialogState accessor, with which // to retrieve the dialog state from the turn context. _dialogs = new DialogSet(accessors.DialogStateAccessor); // Add the prompts we need to the dialog set. _dialogs .Add(new TextPrompt(NamePrompt)) .Add(new NumberPrompt(AgePrompt)) .Add(new ChoicePrompt(SelectionPrompt) { Style = ListStyle.List }); // Add the dialogs we need to the dialog set. _dialogs.Add(new WaterfallDialog(TopLevelDialog) .AddStep(NameStepAsync) .AddStep(AgeStepAsync) .AddStep(StartSelectionStepAsync) .AddStep(AcknowledgementStepAsync)); _dialogs.Add(new WaterfallDialog(ReviewSelectionDialog) .AddStep(SelectionStepAsync) .AddStep(LoopStepAsync)); _dialogs.Add(new WaterfallDialog(OnHoldDialog) .AddStep(OnHoldStepAsync) .AddStep(ContinueToHoldStepAsync)); } /// /// Every conversation turn for our EchoBot will call this method. /// /// A containing all the data needed /// for processing this conversation turn. /// (Optional) A that can be used by other objects /// or threads to receive notice of cancellation. /// A that represents the work queued to execute. /// /// /// public async Task OnTurnAsync( ITurnContext turnContext, CancellationToken cancellationToken = default(CancellationToken)) { if (turnContext == null) { throw new ArgumentNullException(nameof(turnContext)); } // Handle Message activity type, which is the main activity type for shown within a conversational interface // Message activities may contain text, speech, interactive cards, and binary or unknown attachments. // see https://aka.ms/about-bot-activity-message to learn more about the message and other activity types if (turnContext.Activity.Type == ActivityTypes.Message) { string input = turnContext.Activity.Text?.Trim(); // Handle any "global" interruptions before continuing. // On a request for help, display global help. if (string.Equals(input, Interruptions.Help, StringComparison.InvariantCultureIgnoreCase)) { await turnContext.SendActivityAsync(GlobalHelpText, cancellationToken: cancellationToken); return; } DialogContext dialogContext = await _dialogs.CreateContextAsync(turnContext, cancellationToken); // If we're not currently on hold, check whether the user wants to go on hold. if (dialogContext.ActiveDialog?.Id != OnHoldDialog) { if (string.Equals(input, Interruptions.Wait, StringComparison.InvariantCultureIgnoreCase)) { // Transition onto hold. await dialogContext.BeginDialogAsync(OnHoldDialog, null, cancellationToken); await _accessors.ConversationState.SaveChangesAsync(turnContext, false, cancellationToken); return; } } // On a request to cancel, clear the dialog stack completely. if (string.Equals(input, Interruptions.Cancel, StringComparison.InvariantCultureIgnoreCase)) { await dialogContext.CancelAllDialogsAsync(cancellationToken); await turnContext.SendActivityAsync(CancellationText, cancellationToken: cancellationToken); await _accessors.ConversationState.SaveChangesAsync(turnContext, false, cancellationToken); return; } // Run the DialogSet - let the framework identify the current state of the dialog from // the dialog stack and figure out what (if any) is the active dialog. DialogTurnResult results = await dialogContext.ContinueDialogAsync(cancellationToken); switch (results.Status) { case DialogTurnStatus.Cancelled: case DialogTurnStatus.Empty: // If there is no active dialog, we should clear the user info and start a new dialog. await _accessors.UserProfileAccessor.SetAsync(turnContext, new UserProfile(), cancellationToken); await _accessors.UserState.SaveChangesAsync(turnContext, false, cancellationToken); await dialogContext.BeginDialogAsync(TopLevelDialog, null, cancellationToken); break; case DialogTurnStatus.Complete: // If we just finished the dialog, capture and display the results. UserProfile userInfo = results.Result as UserProfile; string status = "You are signed up to review " + (userInfo.CompaniesToReview.Count is 0 ? "no companies" : string.Join(" and ", userInfo.CompaniesToReview)) + "."; await turnContext.SendActivityAsync(status); await _accessors.UserProfileAccessor.SetAsync(turnContext, userInfo, cancellationToken); await _accessors.UserState.SaveChangesAsync(turnContext, false, cancellationToken); break; case DialogTurnStatus.Waiting: // If there is an active dialog, we don't need to do anything here. break; } await _accessors.ConversationState.SaveChangesAsync(turnContext, false, cancellationToken); } // Processes ConversationUpdate Activities to welcome the user. else if (turnContext.Activity.Type == ActivityTypes.ConversationUpdate) { if (turnContext.Activity.MembersAdded != null) { await SendWelcomeMessageAsync(turnContext, cancellationToken); } } else { // Otherwise, note what type of unexpected activity we just received. await turnContext.SendActivityAsync( $"{turnContext.Activity.Type} event detected", cancellationToken: cancellationToken); } } /// /// Sends a welcome message to the user. /// /// A containing all the data needed /// for processing this conversation turn. /// (Optional) A that can be used by other objects /// or threads to receive notice of cancellation. /// A that represents the work queued to execute. private static async Task SendWelcomeMessageAsync( ITurnContext turnContext, CancellationToken cancellationToken) { foreach (ChannelAccount member in turnContext.Activity.MembersAdded) { if (member.Id != turnContext.Activity.Recipient.Id) { Activity reply = turnContext.Activity.CreateReply(); reply.Text = WelcomeText; await turnContext.SendActivityAsync(reply, cancellationToken); } } } /// The first step of the top-level dialog. /// The waterfall step context for the current turn. /// A cancellation token that can be used by other objects /// or threads to receive notice of cancellation. /// A task that represents the work queued to execute. /// If the task is successful, the result contains a to /// communicate some flow control back to the containing WaterfallDialog. private static async Task NameStepAsync( WaterfallStepContext stepContext, CancellationToken cancellationToken) { // Create an object in which to collect the user's information within the dialog. stepContext.Values[UserInfo] = new UserProfile(); Activity prompt = MessageFactory.Text("Please enter your name."); // Ask the user to enter their name. return await stepContext.PromptAsync( NamePrompt, new PromptOptions { Prompt = prompt, RetryPrompt = prompt }, cancellationToken); } /// The second step of the top-level dialog. /// The waterfall step context for the current turn. /// A cancellation token that can be used by other objects /// or threads to receive notice of cancellation. /// A task that represents the work queued to execute. /// If the task is successful, the result contains a to /// communicate some flow control back to the containing WaterfallDialog. private static async Task AgeStepAsync( WaterfallStepContext stepContext, CancellationToken cancellationToken) { // Set the user's name to what they entered in response to the name prompt. ((UserProfile)stepContext.Values[UserInfo]).Name = (string)stepContext.Result; Activity prompt = MessageFactory.Text("Please enter your age."); // Ask the user to enter their age. return await stepContext.PromptAsync( AgePrompt, new PromptOptions { Prompt = prompt, RetryPrompt = prompt }, cancellationToken); } /// The third step of the top-level dialog. /// The waterfall step context for the current turn. /// A cancellation token that can be used by other objects /// or threads to receive notice of cancellation. /// A task that represents the work queued to execute. /// If the task is successful, the result contains a to /// communicate some flow control back to the containing WaterfallDialog. private static async Task StartSelectionStepAsync( WaterfallStepContext stepContext, CancellationToken cancellationToken) { // Set the user's age to what they entered in response to the age prompt. int age = (int)stepContext.Result; ((UserProfile)stepContext.Values[UserInfo]).Age = age; if (age < 25) { // If they are too young, skip the review selection dialog, and pass an empty list to the next step. await stepContext.Context.SendActivityAsync( MessageFactory.Text("You must be 25 or older to participate."), cancellationToken); return await stepContext.NextAsync(new List(), cancellationToken); } else { // Otherwise, start the review selection dialog. return await stepContext.BeginDialogAsync(ReviewSelectionDialog, null, cancellationToken); } } /// The final step of the top-level dialog. /// The waterfall step context for the current turn. /// A cancellation token that can be used by other objects /// or threads to receive notice of cancellation. /// A task that represents the work queued to execute. /// If the task is successful, the result contains a to /// communicate some flow control back to the containing WaterfallDialog. private static async Task AcknowledgementStepAsync( WaterfallStepContext stepContext, CancellationToken cancellationToken) { // Set the user's company selection to what they entered in the review-selection dialog. List list = stepContext.Result as List; var profile = (UserProfile)stepContext.Values[UserInfo]; profile.CompaniesToReview = list ?? new List(); // Thank them for participating. await stepContext.Context.SendActivityAsync( MessageFactory.Text($"Thanks for participating, {profile.Name}."), cancellationToken); // Exit the dialog, returning the collected user information. return await stepContext.EndDialogAsync(stepContext.Values[UserInfo], cancellationToken); } /// The first step of the review-selection dialog. /// The waterfall step context for the current turn. /// A cancellation token that can be used by other objects /// or threads to receive notice of cancellation. /// A task that represents the work queued to execute. /// If the task is successful, the result contains a to /// communicate some flow control back to the containing WaterfallDialog. private static async Task SelectionStepAsync( WaterfallStepContext stepContext, CancellationToken cancellationToken) { // Continue using the same selection list, if any, from the previous iteration of this dialog. List list = stepContext.Options as List ?? new List(); stepContext.Values[CompaniesSelected] = list; // Create a prompt message. string message; if (list.Count is 0) { message = $"Please choose a company to review:"; } else { message = $"You have selected **{list[0]}**. You can review an additional company:"; } // Create the list of options to choose from. List options = Companies.Options.Keys.ToList(); options.AddRange(Interruptions.LocalOptions); if (list.Count > 0) { options.Remove(list[0]); } // Prompt the user for a choice. return await stepContext.PromptAsync( SelectionPrompt, new PromptOptions { Prompt = MessageFactory.Text(message), RetryPrompt = MessageFactory.Text("Please choose an option from the list."), Choices = ChoiceFactory.ToChoices(options), }, cancellationToken); } /// The final step of the review-selection dialog. /// The waterfall step context for the current turn. /// A cancellation token that can be used by other objects /// or threads to receive notice of cancellation. /// A task that represents the work queued to execute. /// If the task is successful, the result contains a to /// communicate some flow control back to the containing WaterfallDialog. private static async Task LoopStepAsync( WaterfallStepContext stepContext, CancellationToken cancellationToken) { // Retrieve their selection list and the choice they juat made. List list = stepContext.Values[CompaniesSelected] as List; FoundChoice choice = (FoundChoice)stepContext.Result; // Handle any local, expected interruptions appropriately. switch (choice.Value) { case Interruptions.Finish: // Exit and return their current selection list. return await stepContext.EndDialogAsync(list, cancellationToken); case Interruptions.Cancel: // Exit and return null. return await stepContext.EndDialogAsync(null, cancellationToken); case Interruptions.Help: // Dispaly the help options. await stepContext.Context.SendActivityAsync( Interruptions.HelpText, cancellationToken: cancellationToken); break; case Interruptions.MoreInfo: // Display more information about the companies. await stepContext.Context.SendActivityAsync( Companies.MoreInfo, cancellationToken: cancellationToken); break; default: // If they chose a company, add it to the list. list.Add(choice.Value); break; } if (list.Count is 2) { // If they've selected 2 companies to review, exit and return their list. return await stepContext.EndDialogAsync(list, cancellationToken); } else { // Otherwise, repeat this dialog, passing in the list from this iteration. return await stepContext.ReplaceDialogAsync(ReviewSelectionDialog, list, cancellationToken); } } /// The first step of the on-hold dialog. /// The waterfall step context for the current turn. /// A cancellation token that can be used by other objects /// or threads to receive notice of cancellation. /// A task that represents the work queued to execute. /// If the task is successful, the result contains a to /// communicate some flow control back to the containing WaterfallDialog. private static async Task OnHoldStepAsync( WaterfallStepContext stepContext, CancellationToken cancellationToken) { string input = stepContext.Context.Activity.Text?.Trim(); if (string.Equals(input, Interruptions.Continue, StringComparison.InvariantCultureIgnoreCase)) { // Exit and return to the last active dialog state. return await stepContext.EndDialogAsync(null, cancellationToken); } else { // Send a status message and let the dialog contnue on the next turn. await stepContext.Context.SendActivityAsync(OnHoldText, cancellationToken: cancellationToken); return Dialog.EndOfTurn; } } /// The last step of the on-hold dialog. /// The waterfall step context for the current turn. /// A cancellation token that can be used by other objects /// or threads to receive notice of cancellation. /// A task that represents the work queued to execute. /// If the task is successful, the result contains a to /// communicate some flow control back to the containing WaterfallDialog. private static async Task ContinueToHoldStepAsync( WaterfallStepContext stepContext, CancellationToken cancellationToken) { // Restart the on-hold dialog. return await stepContext.ReplaceDialogAsync(OnHoldDialog, null, cancellationToken); } } } ================================================ FILE: SDKV4-Samples/dotnet_core/DialogInterruptionsBot/DialogInterruptionsBot.csproj ================================================  netcoreapp2.0 BotBuilder.ruleset Microsoft.BotBuilderSamples Always ================================================ FILE: SDKV4-Samples/dotnet_core/DialogInterruptionsBot/DialogInterruptionsBotAccessors.cs ================================================ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. namespace Microsoft.BotBuilderSamples { using System; using Microsoft.Bot.Builder; using Microsoft.Bot.Builder.Dialogs; /// /// This class is created as a Singleton and passed into the IBot-derived constructor. /// - See constructor for how that is injected. /// - See the Startup.cs file for more details on creating the Singleton that gets /// injected into the constructor. /// public class DialogInterruptionsBotAccessors { /// /// Initializes a new instance of the class. /// Contains the and associated . /// /// The state object that stores the dialog state. /// The state object that stores the user state. public DialogInterruptionsBotAccessors(ConversationState conversationState, UserState userState) { ConversationState = conversationState ?? throw new ArgumentNullException(nameof(conversationState)); UserState = userState ?? throw new ArgumentNullException(nameof(userState)); } /// /// Gets or sets the for ConversationDialogState. /// /// /// The accessor stores the dialog state for the conversation. /// public IStatePropertyAccessor DialogStateAccessor { get; set; } /// /// Gets or sets the for CounterState. /// /// /// The accessor stores user data. /// public IStatePropertyAccessor UserProfileAccessor { get; set; } /// /// Gets the object for the conversation. /// /// The object. public ConversationState ConversationState { get; } /// /// Gets the object for the conversation. /// /// The object. public UserState UserState { get; } } } ================================================ FILE: SDKV4-Samples/dotnet_core/DialogInterruptionsBot/Program.cs ================================================ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. using Microsoft.AspNetCore; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Logging; namespace Microsoft.BotBuilderSamples { public class Program { public static void Main(string[] args) { BuildWebHost(args).Run(); } public static IWebHost BuildWebHost(string[] args) => WebHost.CreateDefaultBuilder(args) .ConfigureLogging((hostingContext, logging) => { // Add Azure Logging logging.AddAzureWebAppDiagnostics(); // Logging Options. // There are other logging options available: // https://docs.microsoft.com/en-us/aspnet/core/fundamentals/logging/?view=aspnetcore-2.1 // logging.AddDebug(); // logging.AddConsole(); }) // Logging Options. // Consider using Application Insights for your logging and metrics needs. // https://azure.microsoft.com/en-us/services/application-insights/ // .UseApplicationInsights() .UseStartup() .Build(); } } ================================================ FILE: SDKV4-Samples/dotnet_core/DialogInterruptionsBot/Properties/launchSettings.json ================================================ { "iisSettings": { "windowsAuthentication": false, "anonymousAuthentication": true, "iisExpress": { "applicationUrl": "http://localhost:3978/", "sslPort": 0 } }, "profiles": { "IIS Express": { "commandName": "IISExpress", "launchBrowser": true, "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } }, "DialogInterruptions_Sample": { "commandName": "Project", "launchBrowser": true, "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" }, "applicationUrl": "http://localhost:3978/" } } } ================================================ FILE: SDKV4-Samples/dotnet_core/DialogInterruptionsBot/README.md ================================================ This sample creates a dialog-based conversation that can be interrupted in various ways in ASP.Net Core 2. # To try this sample - Clone the samples repository ```bash git clone https://github.com/Microsoft/BotFramework-Samples.git ``` - [Optional] Update the `appsettings.json` file under `BotFramework-Samples/SDKV4-Samples/dotnet_core/DialogInterruptionsBot/` with your botFileSecret. For Azure Bot Service bots, you can find the botFileSecret under application settings. # Running Locally ## Visual Studio - Navigate to the samples folder (`BotFramework-Samples/SDKV4-Samples/dotnet_core/DialogInterruptionsBot/`) and open DialogInterruptionsBot.csproj in Visual Studio. - Run the project (press `F5` key). ## .NET Core CLI - Install the [.NET Core CLI tools](https://docs.microsoft.com/dotnet/core/tools/?tabs=netcore2x). - Using the command line, navigate to `BotFramework-Samples/SDKV4-Samples/dotnet_core/DialogInterruptionsBot/` folder. - Type `dotnet run`. ## Testing the bot using Bot Framework Emulator [Microsoft Bot Framework Emulator](https://github.com/microsoft/botframework-emulator) is a desktop application that allows bot developers to test and debug their bots on localhost or running remotely through a tunnel. - Install the Bot Framework emulator from [here](https://aka.ms/botframeworkemulator). ## Connect to bot using Bot Framework Emulator **V4** - Launch the Bot Framework Emulator. - File -> Open bot and navigate to `BotFramework-Samples/SDKV4-Samples/dotnet_core/DialogInterruptionsBot` folder. - Select `complex-dialog.bot` file. # Further reading - [Azure Bot Service](https://docs.microsoft.com/azure/bot-service/bot-service-overview-introduction?view=azure-bot-service-4.0) - [Bot Storage](https://docs.microsoft.com/azure/bot-service/dotnet/bot-builder-dotnet-state?view=azure-bot-service-3.0&viewFallbackFrom=azure-bot-service-4.0) ================================================ FILE: SDKV4-Samples/dotnet_core/DialogInterruptionsBot/Startup.cs ================================================ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. namespace Microsoft.BotBuilderSamples { using System; using System.Linq; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Bot.Builder; using Microsoft.Bot.Builder.Dialogs; using Microsoft.Bot.Builder.Integration.AspNet.Core; using Microsoft.Bot.Configuration; using Microsoft.Bot.Connector.Authentication; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; /// /// The Startup class configures services and the app's request pipeline. /// public class Startup { private ILoggerFactory _loggerFactory; private bool _isProduction = false; public Startup(IHostingEnvironment env) { _isProduction = env.IsProduction(); var builder = new ConfigurationBuilder() .SetBasePath(env.ContentRootPath) .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true) .AddEnvironmentVariables(); Configuration = builder.Build(); } /// /// Gets the configuration that represents a set of key/value application configuration properties. /// /// /// The that represents a set of key/value application configuration properties. /// public IConfiguration Configuration { get; } /// /// This method gets called by the runtime. Use this method to add services to the container. /// /// Specifies the contract for a of service descriptors. /// /// /// public void ConfigureServices(IServiceCollection services) { services.AddBot(options => { var secretKey = Configuration.GetSection("botFileSecret")?.Value; var botFilePath = Configuration.GetSection("botFilePath")?.Value; // Loads .bot configuration file and adds a singleton that your Bot can access through dependency injection. var botConfig = BotConfiguration.Load(botFilePath ?? @".\complex-dialog.bot", secretKey); services.AddSingleton(sp => botConfig ?? throw new InvalidOperationException($"The .bot configuration file could not be loaded. ({botConfig})")); // Retrieve current endpoint. var environment = _isProduction ? "production" : "development"; var service = botConfig.Services.FirstOrDefault(s => s.Type == "endpoint" && s.Name == environment); if (!(service is EndpointService endpointService)) { throw new InvalidOperationException($"The .bot file does not contain an endpoint with name '{environment}'."); } options.CredentialProvider = new SimpleCredentialProvider(endpointService.AppId, endpointService.AppPassword); // Creates a logger for the application to use. ILogger logger = _loggerFactory.CreateLogger(); // Catches any errors that occur during a conversation turn and logs them. options.OnTurnError = async (context, exception) => { logger.LogError($"Exception caught : {exception}"); await context.SendActivityAsync("Sorry, it looks like something went wrong."); }; }); // Create conversation and user state management objects, using memory storage. IStorage dataStore = new MemoryStorage(); var conversationState = new ConversationState(dataStore); var userState = new UserState(dataStore); // Create and register state accessors. // Accessors created here are passed into the IBot-derived class on every turn. services.AddSingleton(sp => { // Create the custom state accessor. // State accessors enable other components to read and write individual properties of state. var accessors = new DialogInterruptionsBotAccessors(conversationState, userState) { DialogStateAccessor = conversationState.CreateProperty("DialogState"), UserProfileAccessor = userState.CreateProperty("UserProfile"), }; return accessors; }); } public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { _loggerFactory = loggerFactory; app.UseDefaultFiles() .UseStaticFiles() .UseBotFramework(); } } } ================================================ FILE: SDKV4-Samples/dotnet_core/DialogInterruptionsBot/UserProfile.cs ================================================ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. namespace Microsoft.BotBuilderSamples { using System.Collections.Generic; /// Contains information about a user. public class UserProfile { /// Gets or sets the user's name. /// The user's name. public string Name { get; set; } /// Gets or sets the user's age. /// The user's age. public int Age { get; set; } /// Gets or sets the list of companies the user wants to review. /// The list of companies the user wants to review. public List CompaniesToReview { get; set; } = new List(); } } ================================================ FILE: SDKV4-Samples/dotnet_core/DialogInterruptionsBot/appsettings.json ================================================ { "botFilePath": "dialog-interruptions.bot", "botFileSecret": "" } ================================================ FILE: SDKV4-Samples/dotnet_core/DialogInterruptionsBot/dialog-interruptions.bot ================================================ { "name": "dialog-interruptions-bot", "services": [ { "type": "endpoint", "name": "development", "endpoint": "http://localhost:3978/api/messages", "appId": "", "appPassword": "", "id": "24" } ], "padlock": "", "version": "2.0" } ================================================ FILE: SDKV4-Samples/dotnet_core/DialogInterruptionsBot/wwwroot/default.htm ================================================  Dialog with interruptions sample
Dialog with Interruptions Bot
Your bot is ready!
You can test your bot in the Bot Framework Emulator
by opening the .bot file in the project folder.
Visit Azure Bot Service to register your bot and add it to
various channels. The bot's endpoint URL typically looks like this:
https://your_bots_hostname/api/messages
================================================ FILE: SDKV4-Samples/dotnet_core/DialogPromptBot/DeploymentScripts/MsbotClone/bot.recipe ================================================ { "version": "1.0", "resources": [ { "type": "endpoint", "id": "1", "name": "development", "url": "http://localhost:3978/api/messages" }, { "type": "endpoint", "id": "2", "name": "production", "url": "https://your-bot-url.azurewebsites.net/api/messages" }, { "type": "abs", "id": "3", "name": "DialogPromptBot-abs" }, { "type": "appInsights", "id": "4", "name": "DialogPromptBot-insights" }, { "type": "blob", "id": "5", "name": "DialogPromptBot-blob", "container": "botstatestore" } ] } ================================================ FILE: SDKV4-Samples/dotnet_core/DialogPromptBot/DialogPromptBot.cs ================================================ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.Bot.Builder; using Microsoft.Bot.Builder.Dialogs; using Microsoft.Bot.Builder.Dialogs.Choices; using Microsoft.Bot.Schema; using Microsoft.Extensions.Logging; namespace Microsoft.BotBuilderSamples { /// /// Represents a bot that processes incoming activities. /// For each user interaction, an instance of this class is created and the OnTurnAsync method /// is called. This is a Transient lifetime service. Transient lifetime services are created /// each time they're requested. For each Activity received, a new instance of this class is /// created. Objects that are expensive to construct, or have a lifetime beyond the single /// turn, should be carefully managed. For example, the object and /// associated object are created with a singleton lifetime. /// public class DialogPromptBot : IBot { // Define identifiers for our dialogs and prompts. private const string ReservationDialog = "reservationDialog"; private const string SizeRangePrompt = "sizeRangePrompt"; private const string LocationPrompt = "locationPrompt"; private const string ReservationDatePrompt = "reservationDatePrompt"; // Define keys for tracked values within the dialog. private const string LocationKey = "location"; private const string PartySizeKey = "partySize"; private readonly DialogSet _dialogSet; private readonly DialogPromptBotAccessors _accessors; private readonly ILogger _logger; /// /// Initializes a new instance of the class. /// /// A class containing used /// to manage state. /// A that is hooked to the Azure /// App Service provider. public DialogPromptBot(DialogPromptBotAccessors accessors, ILoggerFactory loggerFactory) { if (loggerFactory == null) { throw new System.ArgumentNullException(nameof(loggerFactory)); } _logger = loggerFactory.CreateLogger(); _logger.LogTrace("EchoBot turn start."); _accessors = accessors ?? throw new System.ArgumentNullException(nameof(accessors)); // Create the dialog set and add the prompts, including custom validation. _dialogSet = new DialogSet(_accessors.DialogStateAccessor); _dialogSet.Add(new NumberPrompt(SizeRangePrompt, RangeValidatorAsync)); _dialogSet.Add(new ChoicePrompt(LocationPrompt)); _dialogSet.Add(new DateTimePrompt(ReservationDatePrompt, DateValidatorAsync)); // Define the steps of the waterfall dialog and add it to the set. var steps = new WaterfallStep[] { PromptForPartySizeAsync, PromptForLocationAsync, PromptForReservationDateAsync, AcknowledgeReservationAsync, }; _dialogSet.Add(new WaterfallDialog(ReservationDialog, steps)); } /// /// Every conversation turn for our Echo Bot will call this method. /// There are no dialogs used, since it's "single turn" processing, meaning a single /// request and response. /// /// A containing all the data needed /// for processing this conversation turn. /// (Optional) A that can /// be used by other objects or threads to receive notice of cancellation. /// A that represents the work queued to execute. /// /// /// public async Task OnTurnAsync( ITurnContext turnContext, CancellationToken cancellationToken = default(CancellationToken)) { switch (turnContext.Activity.Type) { // On a message from the user: case ActivityTypes.Message: // Get the current reservation info from state. var reservation = await _accessors.ReservationAccessor.GetAsync( turnContext, () => null, cancellationToken); // Generate a dialog context for our dialog set. var dc = await _dialogSet.CreateContextAsync(turnContext, cancellationToken); if (dc.ActiveDialog is null) { // If there is no active dialog, check whether we have a reservation yet. if (reservation is null) { // If not, start the dialog. await dc.BeginDialogAsync(ReservationDialog, null, cancellationToken); } else { // Otherwise, send a status message. await turnContext.SendActivityAsync( $"We'll see you on {reservation.Date}.", cancellationToken: cancellationToken); } } else { // Continue the dialog. var dialogTurnResult = await dc.ContinueDialogAsync(cancellationToken); // If the dialog completed this turn, record the reservation info. if (dialogTurnResult.Status is DialogTurnStatus.Complete) { reservation = (Reservation)dialogTurnResult.Result; await _accessors.ReservationAccessor.SetAsync( turnContext, reservation, cancellationToken); // Send a confirmation message to the user. await turnContext.SendActivityAsync( $"Your party of {reservation.Size} is confirmed for " + $"{reservation.Date} in {reservation.Location}.", cancellationToken: cancellationToken); } } // Save the updated dialog state into the conversation state. await _accessors.ConversationState.SaveChangesAsync( turnContext, false, cancellationToken); break; } } /// First step of the main dialog: prompt for party size. /// The context for the waterfall step. /// A cancellation token that can be used by other objects /// or threads to receive notice of cancellation. /// A task that represents the work queued to execute. /// If the task is successful, the result contains information from this step. private async Task PromptForPartySizeAsync( WaterfallStepContext stepContext, CancellationToken cancellationToken = default(CancellationToken)) { // Prompt for the party size. The result of the prompt is returned to the next step of the waterfall. return await stepContext.PromptAsync( SizeRangePrompt, new PromptOptions { Prompt = MessageFactory.Text("How many people is the reservation for?"), RetryPrompt = MessageFactory.Text("How large is your party?"), Validations = new Range { Min = 3, Max = 8 }, }, cancellationToken); } /// Second step of the main dialog: prompt for location. /// The context for the waterfall step. /// A cancellation token that can be used by other objects /// or threads to receive notice of cancellation. /// A task that represents the work queued to execute. /// If the task is successful, the result contains information from this step. private async Task PromptForLocationAsync( WaterfallStepContext stepContext, CancellationToken cancellationToken) { // Record the party size information in the current dialog state. var size = (int)stepContext.Result; stepContext.Values[PartySizeKey] = size; // Prompt for the location. return await stepContext.PromptAsync( LocationPrompt, new PromptOptions { Prompt = MessageFactory.Text("Please choose a location."), RetryPrompt = MessageFactory.Text("Sorry, please choose a location from the list."), Choices = ChoiceFactory.ToChoices(new List { "Redmond", "Bellevue", "Seattle" }), }, cancellationToken); } /// Third step of the main dialog: prompt for the reservation date. /// The context for the waterfall step. /// A cancellation token that can be used by other objects /// or threads to receive notice of cancellation. /// A task that represents the work queued to execute. /// If the task is successful, the result contains information from this step. private async Task PromptForReservationDateAsync( WaterfallStepContext stepContext, CancellationToken cancellationToken = default(CancellationToken)) { // Record the party size information in the current dialog state. var location = (stepContext.Result as FoundChoice).Value; stepContext.Values[LocationKey] = location; // Prompt for the reservation date. return await stepContext.PromptAsync( ReservationDatePrompt, new PromptOptions { Prompt = MessageFactory.Text("Great. When will the reservation be for?"), RetryPrompt = MessageFactory.Text("What time should we make your reservation for?"), }, cancellationToken); } /// Last step of the main dialog: return the collected reservation information. /// The context for the waterfall step. /// A cancellation token that can be used by other objects /// or threads to receive notice of cancellation. /// A task that represents the work queued to execute. /// If the task is successful, the result contains information from this step. private async Task AcknowledgeReservationAsync( WaterfallStepContext stepContext, CancellationToken cancellationToken = default(CancellationToken)) { // Retrieve the reservation date. var resolution = (stepContext.Result as IList).First(); var time = resolution.Value ?? resolution.Start; // Send an acknowledgement to the user. await stepContext.Context.SendActivityAsync( "Thank you. We will confirm your reservation shortly.", cancellationToken: cancellationToken); // Return the collected information to the parent context. var reservation = new Reservation { Date = time, Size = (int)stepContext.Values[PartySizeKey], Location = (string)stepContext.Values[LocationKey], }; return await stepContext.EndDialogAsync(reservation, cancellationToken); } /// Validates whether the party size is appropriate to make a reservation. /// The validation context. /// A cancellation token that can be used by other objects /// or threads to receive notice of cancellation. /// A task that represents the work queued to execute. /// Reservations can be made for groups of 6 to 20 people. /// If the task is successful, the result indicates whether the input was valid. private async Task RangeValidatorAsync( PromptValidatorContext promptContext, CancellationToken cancellationToken) { // Check whether the input could be recognized as an integer. if (!promptContext.Recognized.Succeeded) { await promptContext.Context.SendActivityAsync( "I'm sorry, I do not understand. Please enter the number of people in your party.", cancellationToken: cancellationToken); return false; } // Check whether the party size is appropriate. var size = promptContext.Recognized.Value; var validRange = promptContext.Options.Validations as Range; if (size < validRange.Min || size > validRange.Max) { await promptContext.Context.SendActivitiesAsync( new Activity[] { MessageFactory.Text($"Sorry, we can only take reservations for parties " + $"of {validRange.Min} to {validRange.Max}."), promptContext.Options.RetryPrompt, }, cancellationToken: cancellationToken); return false; } return true; } /// Validates whether the reservation date is appropriate. /// The validation context. /// A cancellation token that can be used by other objects /// or threads to receive notice of cancellation. /// A task that represents the work queued to execute. /// Reservations must be made at least an hour in advance. /// If the task is successful, the result indicates whether the input was valid. private async Task DateValidatorAsync( PromptValidatorContext> promptContext, CancellationToken cancellationToken = default(CancellationToken)) { // Check whether the input could be recognized as an integer. if (!promptContext.Recognized.Succeeded) { await promptContext.Context.SendActivityAsync( "I'm sorry, I do not understand. Please enter the date or time for your reservation.", cancellationToken: cancellationToken); return false; } // Check whether any of the recognized date-times are appropriate, // and if so, return the first appropriate date-time. var earliest = DateTime.Now.AddHours(1.0); var value = promptContext.Recognized.Value.FirstOrDefault(v => DateTime.TryParse(v.Value ?? v.Start, out var time) && DateTime.Compare(earliest, time) <= 0); if (value != null) { promptContext.Recognized.Value.Clear(); promptContext.Recognized.Value.Add(value); return true; } await promptContext.Context.SendActivityAsync( "I'm sorry, we can't take reservations earlier than an hour from now.", cancellationToken: cancellationToken); return false; } /// Describes an acceptable range of values. public class Range { public int Min { get; set; } public int Max { get; set; } } /// Holds a user's reservation information. public class Reservation { public int Size { get; set; } public string Location { get; set; } public string Date { get; set; } } } } ================================================ FILE: SDKV4-Samples/dotnet_core/DialogPromptBot/DialogPromptBot.csproj ================================================ netcoreapp2.0 DialogPromptBot.ruleset $(OutputPath)$(AssemblyName).xml $(NoWarn),1573,1591,1712 Always ================================================ FILE: SDKV4-Samples/dotnet_core/DialogPromptBot/DialogPromptBot.ruleset ================================================  ================================================ FILE: SDKV4-Samples/dotnet_core/DialogPromptBot/DialogPromptBot.xml ================================================ DialogPromptBot Represents a bot that processes incoming activities. For each user interaction, an instance of this class is created and the OnTurnAsync method is called. This is a Transient lifetime service. Transient lifetime services are created each time they're requested. For each Activity received, a new instance of this class is created. Objects that are expensive to construct, or have a lifetime beyond the single turn, should be carefully managed. For example, the object and associated object are created with a singleton lifetime. Initializes a new instance of the class. A class containing used to manage state. A that is hooked to the Azure App Service provider. Every conversation turn for our Echo Bot will call this method. There are no dialogs used, since it's "single turn" processing, meaning a single request and response. A containing all the data needed for processing this conversation turn. (Optional) A that can be used by other objects or threads to receive notice of cancellation. A that represents the work queued to execute. First step of the main dialog: prompt for party size. The context for the waterfall step. A cancellation token that can be used by other objects or threads to receive notice of cancellation. A task that represents the work queued to execute. If the task is successful, the result contains information from this step. Second step of the main dialog: prompt for location. The context for the waterfall step. A cancellation token that can be used by other objects or threads to receive notice of cancellation. A task that represents the work queued to execute. If the task is successful, the result contains information from this step. Third step of the main dialog: prompt for the reservation date. The context for the waterfall step. A cancellation token that can be used by other objects or threads to receive notice of cancellation. A task that represents the work queued to execute. If the task is successful, the result contains information from this step. Last step of the main dialog: return the collected reservation information. The context for the waterfall step. A cancellation token that can be used by other objects or threads to receive notice of cancellation. A task that represents the work queued to execute. If the task is successful, the result contains information from this step. Validates whether the party size is appropriate to make a reservation. The validation context. A cancellation token that can be used by other objects or threads to receive notice of cancellation. A task that represents the work queued to execute. Reservations can be made for groups of 6 to 20 people. If the task is successful, the result indicates whether the input was valid. Validates whether the reservation date is appropriate. The validation context. A cancellation token that can be used by other objects or threads to receive notice of cancellation. A task that represents the work queued to execute. Reservations must be made at least an hour in advance. If the task is successful, the result indicates whether the input was valid. Describes an acceptable range of values. Holds a user's reservation information. This class is created as a Singleton and passed into the IBot-derived constructor. - See constructor for how that is injected. - See the Startup.cs file for more details on creating the Singleton that gets injected into the constructor. Initializes a new instance of the class. Contains the and associated . The state object that stores the counter. Gets or sets the for CounterState. The accessor stores the turn count for the conversation. Gets the object for the conversation. The object. The Startup class configures services and the request pipeline. Gets the configuration that represents a set of key/value application configuration properties. The that represents a set of key/value application configuration properties. This method gets called by the runtime. Use this method to add services to the container. The specifies the contract for a collection of service descriptors. ================================================ FILE: SDKV4-Samples/dotnet_core/DialogPromptBot/DialogPromptBotAccessors.cs ================================================ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. using System; using Microsoft.Bot.Builder; using Microsoft.Bot.Builder.Dialogs; namespace Microsoft.BotBuilderSamples { /// /// This class is created as a Singleton and passed into the IBot-derived constructor. /// - See constructor for how that is injected. /// - See the Startup.cs file for more details on creating the Singleton that gets /// injected into the constructor. /// public class DialogPromptBotAccessors { /// /// Initializes a new instance of the class. /// Contains the and associated . /// /// The state object that stores the counter. public DialogPromptBotAccessors(ConversationState conversationState) { ConversationState = conversationState ?? throw new ArgumentNullException(nameof(conversationState)); } public static string DialogStateAccessorKey { get; } = "DialogPromptBotAccessors.DialogState"; public static string ReservationAccessorKey { get; } = "DialogPromptBotAccessors.Reservation"; /// /// Gets or sets the for CounterState. /// /// /// The accessor stores the turn count for the conversation. /// public IStatePropertyAccessor DialogStateAccessor { get; set; } public IStatePropertyAccessor ReservationAccessor { get; set; } /// /// Gets the object for the conversation. /// /// The object. public ConversationState ConversationState { get; } } } ================================================ FILE: SDKV4-Samples/dotnet_core/DialogPromptBot/Program.cs ================================================ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. using Microsoft.AspNetCore; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Logging; namespace Microsoft.BotBuilderSamples { public class Program { public static void Main(string[] args) { BuildWebHost(args).Run(); } public static IWebHost BuildWebHost(string[] args) => WebHost.CreateDefaultBuilder(args) .ConfigureLogging((hostingContext, logging) => { // Add Azure Logging logging.AddAzureWebAppDiagnostics(); // Logging Options. // There are other logging options available: // https://docs.microsoft.com/en-us/aspnet/core/fundamentals/logging/?view=aspnetcore-2.1 // logging.AddDebug(); // logging.AddConsole(); }) // Logging Options. // Consider using Application Insights for your logging and metrics needs. // https://azure.microsoft.com/en-us/services/application-insights/ // .UseApplicationInsights() .UseStartup() .Build(); } } ================================================ FILE: SDKV4-Samples/dotnet_core/DialogPromptBot/Properties/launchSettings.json ================================================ { "iisSettings": { "windowsAuthentication": false, "anonymousAuthentication": true, "iisExpress": { "applicationUrl": "http://localhost:3978/", "sslPort": 0 } }, "profiles": { "IIS Express": { "commandName": "IISExpress", "launchBrowser": true, "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } }, "PromptsBot": { "commandName": "Project", "launchBrowser": true, "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" }, "applicationUrl": "http://localhost:3978/" } } } ================================================ FILE: SDKV4-Samples/dotnet_core/DialogPromptBot/README.md ================================================ # Dialog Prompt Bot This sample demonstrates how to use dialog prompts in your ASP.Net Core 2 bot to gather and validate user input. # To try this sample - Clone the samples repository ```bash git clone https://github.com/Microsoft/BotFramework-Samples.git ``` - [Optional] Update the `appsettings.json` file under `BotFramework-Samples\dotnet_core\DialogPromptBot` with your botFileSecret. For Azure Bot Service bots, you can find the botFileSecret under application settings. # Prerequisites ## Visual Studio - Navigate to the samples folder (`BotFramework-Samples\SDKV4-Samples\dotnet_core\DialogPromptBot`) and open DialogPromptBot.csproj in Visual Studio. - Hit F5. ## Visual Studio Code - Open `BotFramework-Samples\SDKV4-Samples\dotnet_core\DialogPromptBot` sample folder. - Bring up a terminal, navigate to `BotFramework-Samples\SDKV4-Samples\dotnet_core\DialogPromptBot` folder. - Type 'dotnet run'. ## Testing the bot using Bot Framework Emulator [Microsoft Bot Framework Emulator](https://github.com/microsoft/botframework-emulator) is a desktop application that allows bot developers to test and debug their bots on localhost or running remotely through a tunnel. - Install the Bot Framework emulator from [here](https://aka.ms/botframeworkemulator). ## Connect to bot using Bot Framework Emulator **V4** - Launch the Bot Framework Emulator. - File -> Open bot and navigate to `BotFramework-Samples\SDKV4-Samples\dotnet_core\DialogPromptBot` folder. - Select `dialog-prompt.bot` file. # Further reading - [Azure Bot Service Introduction](https://docs.microsoft.com/en-us/azure/bot-service/bot-service-overview-introduction?view=azure-bot-service-4.0) - [Bot State](https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-storage-concept?view=azure-bot-service-4.0) - [Managing conversation and user state](https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-howto-v4-state?view=azure-bot-service-4.0&tabs=js) ================================================ FILE: SDKV4-Samples/dotnet_core/DialogPromptBot/Startup.cs ================================================ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. using System; using System.Linq; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Bot.Builder; using Microsoft.Bot.Builder.Dialogs; using Microsoft.Bot.Builder.Integration; using Microsoft.Bot.Builder.Integration.AspNet.Core; using Microsoft.Bot.Configuration; using Microsoft.Bot.Connector.Authentication; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; namespace Microsoft.BotBuilderSamples { /// /// The Startup class configures services and the request pipeline. /// public class Startup { private ILoggerFactory _loggerFactory; private readonly bool _isProduction = false; public Startup(IHostingEnvironment env) { _isProduction = env.IsProduction(); var builder = new ConfigurationBuilder() .SetBasePath(env.ContentRootPath) .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true) .AddEnvironmentVariables(); Configuration = builder.Build(); } /// /// Gets the configuration that represents a set of key/value application configuration properties. /// /// /// The that represents a set of key/value application configuration properties. /// public IConfiguration Configuration { get; } /// /// This method gets called by the runtime. Use this method to add services to the container. /// /// The specifies the contract for a collection of service descriptors. /// public void ConfigureServices(IServiceCollection services) { var secretKey = Configuration.GetSection("botFileSecret")?.Value; var botFilePath = Configuration.GetSection("botFilePath")?.Value; // Loads .bot configuration file and adds a singleton that your Bot can access through dependency injection. var botConfig = BotConfiguration.Load(botFilePath ?? @".\dialog-prompt.bot", secretKey); services.AddSingleton(sp => botConfig ?? throw new InvalidOperationException($"The .bot config file could not be loaded. ({botConfig})")); // The Memory Storage used here is for local bot debugging only. When the bot // is restarted, everything stored in memory will be gone. IStorage dataStore = new MemoryStorage(); // Create Conversation State object. // The Conversation State object is where we persist anything at the conversation-scope. var conversationState = new ConversationState(dataStore); // Create and register state accesssors. // Acessors created here are passed into the IBot-derived class on every turn. services.AddSingleton(sp => { // Create the custom state accessor. // State accessors enable other components to read and write individual properties of state. var accessors = new DialogPromptBotAccessors(conversationState) { DialogStateAccessor = conversationState.CreateProperty( DialogPromptBotAccessors.DialogStateAccessorKey), ReservationAccessor = conversationState.CreateProperty( DialogPromptBotAccessors.ReservationAccessorKey), }; return accessors; }); services.AddBot(options => { // Retrieve current endpoint. var environment = _isProduction ? "production" : "development"; var service = botConfig.Services.Where(s => s.Type == "endpoint" && s.Name == environment).FirstOrDefault(); if (!(service is EndpointService endpointService)) { throw new InvalidOperationException($"The .bot file does not contain an endpoint with name '{environment}'."); } options.CredentialProvider = new SimpleCredentialProvider(endpointService.AppId, endpointService.AppPassword); // Creates a logger for the application to use. ILogger logger = _loggerFactory.CreateLogger(); // Catches any errors that occur during a conversation turn and logs them. options.OnTurnError = async (context, exception) => { logger.LogError($"Exception caught : {exception}"); await context.SendActivityAsync("Sorry, it looks like something went wrong."); }; }); } public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { _loggerFactory = loggerFactory; app.UseDefaultFiles() .UseStaticFiles() .UseBotFramework(); } } } ================================================ FILE: SDKV4-Samples/dotnet_core/DialogPromptBot/appsettings.json ================================================ { "botFilePath": "dialog-prompt.bot", "botFileSecret": "" } ================================================ FILE: SDKV4-Samples/dotnet_core/DialogPromptBot/dialog-prompt.bot ================================================ { "name": "PromptsBot", "services": [ { "type": "endpoint", "name": "development", "endpoint": "http://localhost:3978/api/messages", "appId": "", "appPassword": "", "id": "1" } ], "padlock": "", "version": "2.0" } ================================================ FILE: SDKV4-Samples/dotnet_core/DialogPromptBot/wwwroot/default.htm ================================================  Dialog Prompt Bot sample
Dialog Prompt Bot
Your bot is ready!
You can test your bot in the Bot Framework Emulator
by opening the .bot file in the project folder.
Visit Azure Bot Service to register your bot and add it to
various channels. The bot's endpoint URL typically looks like this:
https://your_bots_hostname/api/messages
================================================ FILE: SDKV4-Samples/dotnet_core/PromptUsersForInput/ConversationFlow.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace Microsoft.BotBuilderSamples { public class ConversationFlow { // Identifies the last question asked. public enum Question { Name, Age, Date, None, // Our last action did not involve a question. } // The last question asked. public Question LastQuestionAsked { get; set; } = Question.None; } } ================================================ FILE: SDKV4-Samples/dotnet_core/PromptUsersForInput/CustomPromptBot.cs ================================================ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using Microsoft.Bot.Builder; using Microsoft.Bot.Schema; using Microsoft.Extensions.Logging; using Microsoft.Recognizers.Text; using Microsoft.Recognizers.Text.DateTime; using Microsoft.Recognizers.Text.Number; namespace Microsoft.BotBuilderSamples { /// /// Represents a bot that processes incoming activities. /// For each user interaction, an instance of this class is created and the OnTurnAsync method is called. /// This is a Transient lifetime service. Transient lifetime services are created /// each time they're requested. For each Activity received, a new instance of this /// class is created. Objects that are expensive to construct, or have a lifetime /// beyond the single turn, should be carefully managed. /// For example, the object and associated /// object are created with a singleton lifetime. /// /// public class CustomPromptBot : IBot { private readonly CustomPromptBotAccessors _accessors; private readonly ILogger _logger; /// /// Initializes a new instance of the class. /// /// A class containing used to manage state. /// A that is hooked to the Azure App Service provider. /// Defines a bot for filling a user profile. /// public CustomPromptBot(CustomPromptBotAccessors accessors, ILoggerFactory loggerFactory) { if (loggerFactory == null) { throw new System.ArgumentNullException(nameof(loggerFactory)); } _logger = loggerFactory.CreateLogger(); _logger.LogTrace("EchoBot turn start."); _accessors = accessors ?? throw new System.ArgumentNullException(nameof(accessors)); } /// The turn handler for the bot. /// A containing all the data needed /// for processing this conversation turn. /// (Optional) A that can be used by other objects /// or threads to receive notice of cancellation. /// A that represents the work queued to execute. public async Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken = default(CancellationToken)) { // Handle Message activity type, which is the main activity type for shown within a conversational interface // Message activities may contain text, speech, interactive cards, and binary or unknown attachments. // see https://aka.ms/about-bot-activity-message to learn more about the message and other activity types if (turnContext.Activity.Type == ActivityTypes.Message) { // Get the state properties from the turn context. ConversationFlow flow = await _accessors.ConversationFlowAccessor.GetAsync(turnContext, () => new ConversationFlow()); UserProfile profile = await _accessors.UserProfileAccessor.GetAsync(turnContext, () => new UserProfile()); await FillOutUserProfileAsync(flow, profile, turnContext); // Update state and save changes. await _accessors.ConversationFlowAccessor.SetAsync(turnContext, flow); await _accessors.ConversationState.SaveChangesAsync(turnContext); await _accessors.UserProfileAccessor.SetAsync(turnContext, profile); await _accessors.UserState.SaveChangesAsync(turnContext); } } /// /// Manages the conversation flow for filling out the user's profile, including parsing and validation. /// /// The conversation flow state property. /// The user profile state property. /// The context object for the current turn. /// A task that represents the work queued to execute. private static async Task FillOutUserProfileAsync(ConversationFlow flow, UserProfile profile, ITurnContext turnContext) { string input = turnContext.Activity.Text?.Trim(); string message; switch (flow.LastQuestionAsked) { case ConversationFlow.Question.None: await turnContext.SendActivityAsync("Let's get started. What is your name?"); flow.LastQuestionAsked = ConversationFlow.Question.Name; break; case ConversationFlow.Question.Name: if (ValidateName(input, out string name, out message)) { profile.Name = name; await turnContext.SendActivityAsync($"Hi {profile.Name}."); await turnContext.SendActivityAsync("How old are you?"); flow.LastQuestionAsked = ConversationFlow.Question.Age; break; } else { await turnContext.SendActivityAsync(message ?? "I'm sorry, I didn't understand that."); break; } case ConversationFlow.Question.Age: if (ValidateAge(input, out int age, out message)) { profile.Age = age; await turnContext.SendActivityAsync($"I have your age as {profile.Age}."); await turnContext.SendActivityAsync("When is your flight?"); flow.LastQuestionAsked = ConversationFlow.Question.Date; break; } else { await turnContext.SendActivityAsync(message ?? "I'm sorry, I didn't understand that."); break; } case ConversationFlow.Question.Date: if (ValidateDate(input, out string date, out message)) { profile.Date = date; await turnContext.SendActivityAsync($"Your cab ride to the airport is scheduled for {profile.Date}."); await turnContext.SendActivityAsync($"Thanks for completing the booking {profile.Name}."); await turnContext.SendActivityAsync($"Type anything to run the bot again."); flow.LastQuestionAsked = ConversationFlow.Question.None; profile = new UserProfile(); break; } else { await turnContext.SendActivityAsync(message ?? "I'm sorry, I didn't understand that."); break; } } } /// /// Validates name input. /// /// The user's input. /// When the method returns, contains the normalized name, if validation succeeded. /// When the method returns, contains a message with which to reprompt, if validation failed. /// indicates whether validation succeeded. private static bool ValidateName(string input, out string name, out string message) { name = null; message = null; if (string.IsNullOrWhiteSpace(input)) { message = "Please enter a name that contains at least one character."; } else { name = input.Trim(); } return message is null; } /// /// Validates age input. /// /// The user's input. /// When the method returns, contains the normalized age, if validation succeeded. /// When the method returns, contains a message with which to reprompt, if validation failed. /// indicates whether validation succeeded. private static bool ValidateAge(string input, out int age, out string message) { age = 0; message = null; // Try to recognize the input as a number. This works for responses such as "twelve" as well as "12". try { // Attempt to convert the Recognizer result to an integer. This works for "a dozen", "twelve", "12", and so on. // The recognizer returns a list of potential recognition results, if any. List results = NumberRecognizer.RecognizeNumber(input, Culture.English); foreach (ModelResult result in results) { // The result resolution is a dictionary, where the "value" entry contains the processed string. if (result.Resolution.TryGetValue("value", out object value)) { age = Convert.ToInt32(value); if (age >= 18 && age <= 120) { return true; } } } message = "Please enter an age between 18 and 120."; } catch { message = "I'm sorry, I could not interpret that as an age. Please enter an age between 18 and 120."; } return message is null; } /// /// Validates flight time input. /// /// The user's input. /// When the method returns, contains the normalized date, if validation succeeded. /// When the method returns, contains a message with which to reprompt, if validation failed. /// indicates whether validation succeeded. private static bool ValidateDate(string input, out string date, out string message) { date = null; message = null; // Try to recognize the input as a date-time. This works for responses such as "11/14/2018", "9pm", "tomorrow", "Sunday at 5pm", and so on. // The recognizer returns a list of potential recognition results, if any. try { List results = DateTimeRecognizer.RecognizeDateTime(input, Culture.English); // Check whether any of the recognized date-times are appropriate, // and if so, return the first appropriate date-time. We're checking for a value at least an hour in the future. DateTime earliest = DateTime.Now.AddHours(1.0); foreach (ModelResult result in results) { // The result resolution is a dictionary, where the "values" entry contains the processed input. List> resolutions = result.Resolution["values"] as List>; foreach (Dictionary resolution in resolutions) { // The processed input contains a "value" entry if it is a date-time value, or "start" and // "end" entries if it is a date-time range. if (resolution.TryGetValue("value", out string dateString) || resolution.TryGetValue("start", out dateString)) { if (DateTime.TryParse(dateString, out DateTime candidate) && earliest < candidate) { date = candidate.ToShortDateString(); return true; } } } } message = "I'm sorry, please enter a date at least an hour out."; } catch { message = "I'm sorry, I could not interpret that as an appropriate date. Please enter a date at least an hour out."; } return false; } } } ================================================ FILE: SDKV4-Samples/dotnet_core/PromptUsersForInput/CustomPromptBot.ruleset ================================================  ================================================ FILE: SDKV4-Samples/dotnet_core/PromptUsersForInput/CustomPromptBotAccessors.cs ================================================ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. using System; using Microsoft.Bot.Builder; namespace Microsoft.BotBuilderSamples { /// /// This class is created as a Singleton and passed into the IBot-derived constructor. /// - See constructor for how that is injected. /// - See the Startup.cs file for more details on creating the Singleton that gets /// injected into the constructor. /// public class CustomPromptBotAccessors { /// /// Initializes a new instance of the class. /// Contains the state management and associated accessor objects. /// /// The state object that stores the conversation state. /// The state object that stores the user state. public CustomPromptBotAccessors(ConversationState conversationState, UserState userState) { ConversationState = conversationState ?? throw new ArgumentNullException(nameof(conversationState)); UserState = userState ?? throw new ArgumentNullException(nameof(userState)); } /// /// Gets the accessor name for the conversation flow property. /// /// The accessor name for the conversation flow property. /// Accessors require a unique name. public static string ConversationFlowName { get; } = "ConversationFlow"; /// /// Gets the accessor name for the user profile property accessor. /// /// The accessor name for the user profile property accessor. /// Accessors require a unique name. public static string UserProfileName { get; } = "UserProfile"; /// /// Gets or sets the for the conversation flow property. /// /// /// The accessor stores the turn count for the conversation. /// public IStatePropertyAccessor ConversationFlowAccessor { get; set; } /// /// Gets or sets the for the user profile property. /// /// /// The accessor stores the turn count for the conversation. /// public IStatePropertyAccessor UserProfileAccessor { get; set; } /// /// Gets the object for the conversation. /// /// The object. public ConversationState ConversationState { get; } /// /// Gets the object for the conversation. /// /// The object. public UserState UserState { get; } } } ================================================ FILE: SDKV4-Samples/dotnet_core/PromptUsersForInput/DeploymentScripts/MsbotClone/bot.recipe ================================================ { "version": "1.0", "resources": [ { "type": "endpoint", "id": "1", "name": "development", "url": "http://localhost:3978/api/messages" }, { "type": "endpoint", "id": "2", "name": "production", "url": "https://your-bot-url.azurewebsites.net/api/messages" }, { "type": "abs", "id": "3", "name": "CustomPromptBot-abs" }, { "type": "appInsights", "id": "4", "name": "CustomPromptBot-insights" }, { "type": "blob", "id": "5", "name": "CustomPromptBot-blob", "container": "botstatestore" } ] } ================================================ FILE: SDKV4-Samples/dotnet_core/PromptUsersForInput/Program.cs ================================================ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. using Microsoft.AspNetCore; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Logging; namespace Microsoft.BotBuilderSamples { public class Program { public static void Main(string[] args) { BuildWebHost(args).Run(); } public static IWebHost BuildWebHost(string[] args) => WebHost.CreateDefaultBuilder(args) .ConfigureLogging((hostingContext, logging) => { // Add Azure Logging logging.AddAzureWebAppDiagnostics(); // Logging Options. // There are other logging options available: // https://docs.microsoft.com/en-us/aspnet/core/fundamentals/logging/?view=aspnetcore-2.1 // logging.AddDebug(); // logging.AddConsole(); }) // Logging Options. // Consider using Application Insights for your logging and metrics needs. // https://azure.microsoft.com/en-us/services/application-insights/ // .UseApplicationInsights() .UseStartup() .Build(); } } ================================================ FILE: SDKV4-Samples/dotnet_core/PromptUsersForInput/PromptUsersForInput.csproj ================================================  netcoreapp2.0 CustomPromptBot.ruleset Always ================================================ FILE: SDKV4-Samples/dotnet_core/PromptUsersForInput/Properties/launchSettings.json ================================================ { "iisSettings": { "windowsAuthentication": false, "anonymousAuthentication": true, "iisExpress": { "applicationUrl": "http://localhost:3978/", "sslPort": 0 } }, "profiles": { "IIS Express": { "commandName": "IISExpress", "launchBrowser": true, "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } }, "Bot_Builder_Echo_Bot_V43": { "commandName": "Project", "launchBrowser": true, "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" }, "applicationUrl": "http://localhost:3978/" } } } ================================================ FILE: SDKV4-Samples/dotnet_core/PromptUsersForInput/README.md ================================================ # Prompt users for input This sample demonstrates how to create your own prompts with an ASP.Net Core 2 bot. The bot maintains conversation state to track and direct the conversation and ask the user questions. The bot maintains user state to track the user's answers. # To try this sample - Clone the samples repository ```bash git clone https://github.com/Microsoft/BotFramework-Samples.git ``` - [Optional] Update the `appsettings.json` file under `BotFramework-Samples\SDKV4-Samples\dotnet_core\PromptUsersForInput\` with your botFileSecret. # Prerequisites ## Visual Studio - Navigate to the v4 C# samples folder (`BotFramework-Samples\SDKV4-Samples\dotnet_core\PromptUsersForInput`) and open the PromptUsersForInput.csproj in Visual Studio. - Hit F5. ## Visual Studio Code - Open the `BotFramework-Samples\SDKV4-Samples\dotnet_core\PromptUsersForInput` folder. - Bring up a terminal, navigate to `BotFramework-Samples\SDKV4-Samples\dotnet_core\PromptUsersForInput` folder. - Type 'dotnet run'. ## Testing the bot using Bot Framework Emulator [Microsoft Bot Framework Emulator](https://github.com/microsoft/botframework-emulator) is a desktop application that allows bot developers to test and debug their bots on localhost or running remotely through a tunnel. - Install the Bot Framework emulator from [here](https://aka.ms/botframeworkemulator). ## Connect to bot using Bot Framework Emulator **V4** - Launch the Bot Framework Emulator. - Select the **File** > **Open bot configuration** menu item and navigate to the `BotFramework-Samples\SDKV4-Samples\dotnet_core\PromptUsersForInput` folder. - Select the `custom-prompt.bot` file. # Bot state A bot is inherently stateless. Once your bot is deployed, it may not run in the same process or on the same machine from one turn to the next. However, your bot may need to track the context of a conversation, so that it can manage its behavior and remember answers to previous questions. In this example, the bot's state is used to a track number of messages. - We use the bot's turn handler and user and conversation state properties to manage the flow of the conversation and the collection of input. - We ask the user a series of questions; parse, validate, and normalize their answers; and then save their input. This sample is intended to be run and tested locally and is not designed to be deployed to Azure. # Further reading - [Azure Bot Service Introduction](https://docs.microsoft.com/azure/bot-service/bot-service-overview-introduction) ================================================ FILE: SDKV4-Samples/dotnet_core/PromptUsersForInput/Startup.cs ================================================ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. using System; using System.Linq; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Bot.Builder; using Microsoft.Bot.Builder.Integration; using Microsoft.Bot.Builder.Integration.AspNet.Core; using Microsoft.Bot.Configuration; using Microsoft.Bot.Connector.Authentication; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; namespace Microsoft.BotBuilderSamples { /// /// The Startup class configures services and the request pipeline. /// public class Startup { private ILoggerFactory _loggerFactory; private readonly bool _isProduction = false; public Startup(IHostingEnvironment env) { _isProduction = env.IsProduction(); IConfigurationBuilder builder = new ConfigurationBuilder() .SetBasePath(env.ContentRootPath) .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true) .AddEnvironmentVariables(); Configuration = builder.Build(); } /// /// Gets the configuration that represents a set of key/value application configuration properties. /// /// /// The that represents a set of key/value application configuration properties. /// public IConfiguration Configuration { get; } /// /// This method gets called by the runtime. Use this method to add services to the container. /// /// The specifies the contract for a collection of service descriptors. /// /// /// public void ConfigureServices(IServiceCollection services) { services.AddBot(options => { string secretKey = Configuration.GetSection("botFileSecret")?.Value; string botFilePath = Configuration.GetSection("botFilePath")?.Value; // Loads .bot configuration file and adds a singleton that your Bot can access through dependency injection. BotConfiguration botConfig = BotConfiguration.Load(botFilePath ?? @".\custom-prompt.bot", secretKey); services.AddSingleton(sp => botConfig ?? throw new InvalidOperationException($"The .bot config file could not be loaded. ({botConfig})")); // Retrieve current endpoint. string environment = _isProduction ? "production" : "development"; ConnectedService service = botConfig.Services.Where(s => s.Type == "endpoint" && s.Name == environment).FirstOrDefault(); if (!(service is EndpointService endpointService)) { throw new InvalidOperationException($"The .bot file does not contain an endpoint with name '{environment}'."); } options.CredentialProvider = new SimpleCredentialProvider(endpointService.AppId, endpointService.AppPassword); // Creates a logger for the application to use. ILogger logger = _loggerFactory.CreateLogger(); // Catches any errors that occur during a conversation turn and logs them. options.OnTurnError = async (context, exception) => { logger.LogError($"Exception caught : {exception}"); await context.SendActivityAsync("Sorry, it looks like something went wrong."); }; }); // Create conversation and user state with in-memory storage provider. IStorage storage = new MemoryStorage(); ConversationState conversationState = new ConversationState(storage); UserState userState = new UserState(storage); // Create and register state accessors. // Accessors created here are passed into the IBot-derived class on every turn. services.AddSingleton(sp => { // Create the custom state accessor. return new CustomPromptBotAccessors(conversationState, userState) { ConversationFlowAccessor = conversationState.CreateProperty(CustomPromptBotAccessors.ConversationFlowName), UserProfileAccessor = userState.CreateProperty(CustomPromptBotAccessors.UserProfileName), }; }); } public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { _loggerFactory = loggerFactory; app.UseDefaultFiles() .UseStaticFiles() .UseBotFramework(); } } } ================================================ FILE: SDKV4-Samples/dotnet_core/PromptUsersForInput/UserProfile.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace Microsoft.BotBuilderSamples { public class UserProfile { public string Name { get; set; } public int? Age { get; set; } public string Date { get; set; } } } ================================================ FILE: SDKV4-Samples/dotnet_core/PromptUsersForInput/appsettings.json ================================================ { "botFilePath": "custom-prompt.bot", "botFileSecret": "" } ================================================ FILE: SDKV4-Samples/dotnet_core/PromptUsersForInput/custom-prompt.bot ================================================ { "name": "CustomPromptBot", "services": [ { "type": "endpoint", "name": "development", "endpoint": "http://localhost:3978/api/messages", "appId": "", "appPassword": "", "id": "1" } ], "padlock": "", "version": "2.0" } ================================================ FILE: SDKV4-Samples/dotnet_core/PromptUsersForInput/wwwroot/default.htm ================================================  Custom Prompt Bot sample
Custom Prompt Bot
Your bot is ready!
You can test your bot in the Bot Framework Emulator
by opening the .bot file in the project folder.
Visit Azure Bot Service to register your bot and add it to
various channels. The bot's endpoint URL typically looks like this:
https://your_bots_hostname/api/messages
================================================ FILE: SDKV4-Samples/dotnet_core/StateBot/ConversationData.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace Microsoft.BotBuilderSamples { // Defines a state property used to track conversation data. public class ConversationData { // The time-stamp of the most recent incoming message. public string Timestamp { get; set; } // The ID of the user's channel. public string ChannelId { get; set; } // Track whether we have already asked the user's name public bool PromptedUserForName { get; set; } = false; } } ================================================ FILE: SDKV4-Samples/dotnet_core/StateBot/DeploymentScripts/MsbotClone/bot.recipe ================================================ { "version": "1.0", "resources": [ { "type": "endpoint", "id": "1", "name": "development", "url": "http://localhost:3978/api/messages" }, { "type": "endpoint", "id": "2", "name": "production", "url": "https://your-bot-url.azurewebsites.net/api/messages" }, { "type": "abs", "id": "3", "name": "StateBot-abs" }, { "type": "appInsights", "id": "4", "name": "StateBot-insights" }, { "type": "blob", "id": "5", "name": "StateBot-blob", "container": "botstatestore" } ] } ================================================ FILE: SDKV4-Samples/dotnet_core/StateBot/Program.cs ================================================ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. using Microsoft.AspNetCore; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Logging; namespace Microsoft.BotBuilderSamples { public class Program { public static void Main(string[] args) { BuildWebHost(args).Run(); } public static IWebHost BuildWebHost(string[] args) => WebHost.CreateDefaultBuilder(args) .ConfigureLogging((hostingContext, logging) => { // Add Azure Logging logging.AddAzureWebAppDiagnostics(); // Logging Options. // There are other logging options available: // https://docs.microsoft.com/en-us/aspnet/core/fundamentals/logging/?view=aspnetcore-2.1 // logging.AddDebug(); // logging.AddConsole(); }) // Logging Options. // Consider using Application Insights for your logging and metrics needs. // https://azure.microsoft.com/en-us/services/application-insights/ // .UseApplicationInsights() .UseStartup() .Build(); } } ================================================ FILE: SDKV4-Samples/dotnet_core/StateBot/Properties/launchSettings.json ================================================ { "iisSettings": { "windowsAuthentication": false, "anonymousAuthentication": true, "iisExpress": { "applicationUrl": "http://localhost:3978/", "sslPort": 0 } }, "profiles": { "IIS Express": { "commandName": "IISExpress", "launchBrowser": true, "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } }, "StateBot": { "commandName": "Project", "launchBrowser": true, "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" }, "applicationUrl": "http://localhost:3978/" } } } ================================================ FILE: SDKV4-Samples/dotnet_core/StateBot/README.md ================================================ # Save user and conversation data This sample demonstrates how to save user and conversation data in an ASP.Net Core 2 bot. The bot maintains conversation state to track and direct the conversation and ask the user questions. The bot maintains user state to track the user's answers. # To try this sample - Clone the samples repository ```bash git clone https://github.com/Microsoft/BotFramework-Samples.git ``` - [Optional] Update the `appsettings.json` file under `BotFramework-Samples\SDKV4-Samples\dotnet_core\BotState\` with your botFileSecret. # Prerequisites ## Visual Studio - Navigate to the v4 C# samples folder (`BotFramework-Samples\SDKV4-Samples\dotnet_core\BotState`) and open the PromptUsersForInput.csproj in Visual Studio. - Hit F5. ## Visual Studio Code - Open the `BotFramework-Samples\SDKV4-Samples\dotnet_core\BotState` folder. - Bring up a terminal, navigate to `BotFramework-Samples\SDKV4-Samples\dotnet_core\BotState` folder. - Type 'dotnet run'. ## Testing the bot using Bot Framework Emulator [Microsoft Bot Framework Emulator](https://github.com/microsoft/botframework-emulator) is a desktop application that allows bot developers to test and debug their bots on localhost or running remotely through a tunnel. - Install the Bot Framework emulator from [here](https://aka.ms/botframeworkemulator). ## Connect to bot using Bot Framework Emulator **V4** - Launch the Bot Framework Emulator. - Select the **File** > **Open bot configuration** menu item and navigate to the `BotFramework-Samples\SDKV4-Samples\dotnet_core\BotState` folder. - Select the `state.bot` file. ================================================ FILE: SDKV4-Samples/dotnet_core/StateBot/Startup.cs ================================================ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. using System; using System.Linq; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Bot.Builder; using Microsoft.Bot.Builder.Integration; using Microsoft.Bot.Builder.Integration.AspNet.Core; using Microsoft.Bot.Configuration; using Microsoft.Bot.Connector.Authentication; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; namespace Microsoft.BotBuilderSamples { /// /// The Startup class configures services and the request pipeline. /// public class Startup { private readonly bool _isProduction = false; private ILoggerFactory _loggerFactory; public Startup(IHostingEnvironment env) { _isProduction = env.IsProduction(); IConfigurationBuilder builder = new ConfigurationBuilder() .SetBasePath(env.ContentRootPath) .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true) .AddEnvironmentVariables(); Configuration = builder.Build(); } /// /// Gets the configuration that represents a set of key/value application configuration properties. /// /// /// The that represents a set of key/value application configuration properties. /// public IConfiguration Configuration { get; } /// /// This method gets called by the runtime. Use this method to add services to the container. /// /// The specifies the contract for a collection of service descriptors. /// /// /// public void ConfigureServices(IServiceCollection services) { services.AddBot(options => { string secretKey = Configuration.GetSection("botFileSecret")?.Value; string botFilePath = Configuration.GetSection("botFilePath")?.Value; // Loads .bot configuration file and adds a singleton that your Bot can access through dependency injection. BotConfiguration botConfig = BotConfiguration.Load(botFilePath ?? @".\State.bot", secretKey); services.AddSingleton(sp => botConfig ?? throw new InvalidOperationException($"The .bot config file could not be loaded. ({botConfig})")); // Retrieve current endpoint. string environment = _isProduction ? "production" : "development"; ConnectedService service = botConfig.Services.Where(s => s.Type == "endpoint" && s.Name == environment).FirstOrDefault(); if (!(service is EndpointService endpointService)) { throw new InvalidOperationException($"The .bot file does not contain an endpoint with name '{environment}'."); } options.CredentialProvider = new SimpleCredentialProvider(endpointService.AppId, endpointService.AppPassword); // Creates a logger for the application to use. ILogger logger = _loggerFactory.CreateLogger(); // Catches any errors that occur during a conversation turn and logs them. options.OnTurnError = async (context, exception) => { logger.LogError($"Exception caught : {exception}"); await context.SendActivityAsync("Sorry, it looks like something went wrong."); }; }); // Create conversation and user state with in-memory storage provider. IStorage storage = new MemoryStorage(); ConversationState conversationState = new ConversationState(storage); UserState userState = new UserState(storage); // Create and register state accessors. // Accessors created here are passed into the IBot-derived class on every turn. services.AddSingleton(sp => { // Create the custom state accessor. return new StateBotAccessors(conversationState, userState) { ConversationDataAccessor = conversationState.CreateProperty(StateBotAccessors.ConversationDataName), UserProfileAccessor = userState.CreateProperty(StateBotAccessors.UserProfileName), }; }); } public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { _loggerFactory = loggerFactory; app.UseDefaultFiles() .UseStaticFiles() .UseBotFramework(); } } } ================================================ FILE: SDKV4-Samples/dotnet_core/StateBot/StateBot.cs ================================================ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using Microsoft.Bot.Builder; using Microsoft.Bot.Schema; using Microsoft.Extensions.Logging; namespace Microsoft.BotBuilderSamples { /// /// Represents a bot that processes incoming activities. /// For each user interaction, an instance of this class is created and the OnTurnAsync method is called. /// This is a Transient lifetime service. Transient lifetime services are created /// each time they're requested. For each Activity received, a new instance of this /// class is created. Objects that are expensive to construct, or have a lifetime /// beyond the single turn, should be carefully managed. /// For example, the object and associated /// object are created with a singleton lifetime. /// public class StateBot : IBot { private readonly StateBotAccessors _accessors; private readonly ILogger _logger; /// /// Initializes a new instance of the class. /// /// A class containing used to manage state. /// A that is hooked to the Azure App Service provider. /// Defines a bot for filling a user profile. public StateBot(StateBotAccessors accessors, ILoggerFactory loggerFactory) { if (loggerFactory == null) { throw new System.ArgumentNullException(nameof(loggerFactory)); } _logger = loggerFactory.CreateLogger(); _logger.LogTrace("EchoBot turn start."); _accessors = accessors ?? throw new System.ArgumentNullException(nameof(accessors)); } /// The turn handler for the bot. /// A containing all the data needed /// for processing this conversation turn. /// (Optional) A that can be used by other objects /// or threads to receive notice of cancellation. /// A that represents the work queued to execute. public async Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken = default(CancellationToken)) { if (turnContext.Activity.Type == ActivityTypes.Message) { // Get the state properties from the turn context. UserProfile userProfile = await _accessors.UserProfileAccessor.GetAsync(turnContext, () => new UserProfile()); ConversationData conversationData = await _accessors.ConversationDataAccessor.GetAsync(turnContext, () => new ConversationData()); if (string.IsNullOrEmpty(userProfile.Name)) { // First time around this is set to false, so we will prompt user for name. if (conversationData.PromptedUserForName) { // Set the name to what the user provided. userProfile.Name = turnContext.Activity.Text?.Trim(); // Acknowledge that we got their name. await turnContext.SendActivityAsync($"Thanks {userProfile.Name}."); // Reset the flag to allow the bot to go though the cycle again. conversationData.PromptedUserForName = false; } else { // Prompt the user for their name. await turnContext.SendActivityAsync($"What is your name?"); // Set the flag to true, so we don't prompt in the next turn. conversationData.PromptedUserForName = true; } // Save user state and save changes. await _accessors.UserProfileAccessor.SetAsync(turnContext, userProfile); await _accessors.UserState.SaveChangesAsync(turnContext); } else { // Add message details to the conversation data. conversationData.Timestamp = turnContext.Activity.Timestamp.ToString(); conversationData.ChannelId = turnContext.Activity.ChannelId.ToString(); // Display state data. await turnContext.SendActivityAsync($"{userProfile.Name} sent: {turnContext.Activity.Text}"); await turnContext.SendActivityAsync($"Message received at: {conversationData.Timestamp}"); await turnContext.SendActivityAsync($"Message received from: {conversationData.ChannelId}"); } // Update conversation state and save changes. await _accessors.ConversationDataAccessor.SetAsync(turnContext, conversationData); await _accessors.ConversationState.SaveChangesAsync(turnContext); } } } } ================================================ FILE: SDKV4-Samples/dotnet_core/StateBot/StateBot.csproj ================================================  netcoreapp2.0 StateBot.ruleset $(OutputPath)$(AssemblyName).xml $(NoWarn),1573,1591,1712 Always ================================================ FILE: SDKV4-Samples/dotnet_core/StateBot/StateBot.ruleset ================================================  ================================================ FILE: SDKV4-Samples/dotnet_core/StateBot/StateBot.sln ================================================  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 VisualStudioVersion = 15.0.27428.2043 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StateBot", "StateBot.csproj", "{70EECF7F-1978-4587-9E6D-6371EC3A727B}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {70EECF7F-1978-4587-9E6D-6371EC3A727B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {70EECF7F-1978-4587-9E6D-6371EC3A727B}.Debug|Any CPU.Build.0 = Debug|Any CPU {70EECF7F-1978-4587-9E6D-6371EC3A727B}.Release|Any CPU.ActiveCfg = Release|Any CPU {70EECF7F-1978-4587-9E6D-6371EC3A727B}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {D6A9E094-666B-42FD-A3C9-2929F55D978E} EndGlobalSection EndGlobal ================================================ FILE: SDKV4-Samples/dotnet_core/StateBot/StateBot.xml ================================================ StateBot The Startup class configures services and the request pipeline. Gets the configuration that represents a set of key/value application configuration properties. The that represents a set of key/value application configuration properties. This method gets called by the runtime. Use this method to add services to the container. The specifies the contract for a collection of service descriptors. Represents a bot that processes incoming activities. For each user interaction, an instance of this class is created and the OnTurnAsync method is called. This is a Transient lifetime service. Transient lifetime services are created each time they're requested. For each Activity received, a new instance of this class is created. Objects that are expensive to construct, or have a lifetime beyond the single turn, should be carefully managed. For example, the object and associated object are created with a singleton lifetime. Initializes a new instance of the class. A class containing used to manage state. A that is hooked to the Azure App Service provider. Defines a bot for filling a user profile. The turn handler for the bot. A containing all the data needed for processing this conversation turn. (Optional) A that can be used by other objects or threads to receive notice of cancellation. A that represents the work queued to execute. This class is created as a Singleton and passed into the IBot-derived constructor. - See constructor for how that is injected. - See the Startup.cs file for more details on creating the Singleton that gets injected into the constructor. Initializes a new instance of the class. Contains the state management and associated accessor objects. The state object that stores the conversation state. The state object that stores the user state. Gets the accessor name for the user profile property accessor. The accessor name for the user profile property accessor. Accessors require a unique name. Gets or sets the for the user profile property. The accessor stores the turn count for the conversation. Gets the object for the conversation. The object. Gets the object for the conversation. The object. ================================================ FILE: SDKV4-Samples/dotnet_core/StateBot/StateBotAccessors.cs ================================================ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. using System; using Microsoft.Bot.Builder; namespace Microsoft.BotBuilderSamples { /// /// This class is created as a Singleton and passed into the IBot-derived constructor. /// - See constructor for how that is injected. /// - See the Startup.cs file for more details on creating the Singleton that gets /// injected into the constructor. /// public class StateBotAccessors { /// /// Initializes a new instance of the class. /// Contains the state management and associated accessor objects. /// /// The state object that stores the conversation state. /// The state object that stores the user state. public StateBotAccessors(ConversationState conversationState, UserState userState) { ConversationState = conversationState ?? throw new ArgumentNullException(nameof(conversationState)); UserState = userState ?? throw new ArgumentNullException(nameof(userState)); } /// /// Gets the accessor name for the user profile property accessor. /// /// The accessor name for the user profile property accessor. /// Accessors require a unique name. public static string UserProfileName { get; } = "UserProfile"; /// /// Gets the accessor name for the conversation data property accessor. /// /// The accessor name for the conversation data property accessor. /// Accessors require a unique name. public static string ConversationDataName { get; } = "ConversationData"; /// /// Gets or sets the for the user profile property. /// /// /// The accessor for the user profile property. /// public IStatePropertyAccessor UserProfileAccessor { get; set; } /// /// Gets or sets the for the conversation data property. /// /// /// The accessor for the conversation data property. /// public IStatePropertyAccessor ConversationDataAccessor { get; set; } /// /// Gets the object for the conversation. /// /// The object. public ConversationState ConversationState { get; } /// /// Gets the object for the bot. /// /// The object. public UserState UserState { get; } } } ================================================ FILE: SDKV4-Samples/dotnet_core/StateBot/UserProfile.cs ================================================ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace Microsoft.BotBuilderSamples { // Defines a state property used to track information about the user. public class UserProfile { public string Name { get; set; } } } ================================================ FILE: SDKV4-Samples/dotnet_core/StateBot/appsettings.json ================================================ { "botFilePath": "state.bot", "botFileSecret": "" } ================================================ FILE: SDKV4-Samples/dotnet_core/StateBot/state.bot ================================================ { "name": "StateBot", "services": [ { "type": "endpoint", "name": "development", "endpoint": "http://localhost:3978/api/messages", "appId": "", "appPassword": "", "id": "1" } ], "padlock": "", "version": "2.0" } ================================================ FILE: SDKV4-Samples/dotnet_core/StateBot/wwwroot/default.htm ================================================  State Bot sample
State Bot
Your bot is ready!
You can test your bot in the Bot Framework Emulator
by opening the .bot file in the project folder.
Visit Azure Bot Service to register your bot and add it to
various channels. The bot's endpoint URL typically looks like this:
https://your_bots_hostname/api/messages
================================================ FILE: SDKV4-Samples/dotnet_core/nlp-with-dispatch/QnAMaker.tsv ================================================ Question Answer Source Metadata hi Hello! QnAMaker.tsv greetings Hello! QnAMaker.tsv good morning Hello! QnAMaker.tsv good evening Hello! QnAMaker.tsv What are you? I am the LUIS-QnAMaker Dispatch bot! This sample demonstrates using several LUIS applications and QnA Maker knowledge base using dispatch. QnAMaker.tsv What? I am the LUIS-QnAMaker Dispatch bot! This sample demonstrates using several LUIS applications and QnA Maker knowledge base using dispatch. QnAMaker.tsv What do you do? I am the LUIS-QnAMaker Dispatch bot! This sample demonstrates using several LUIS applications and QnA Maker knowledge base using dispatch. QnAMaker.tsv Who are you? I am the LUIS-QnAMaker Dispatch bot! This sample demonstrates using several LUIS applications and QnA Maker knowledge base using dispatch. QnAMaker.tsv What is your name? I am the LUIS-QnAMaker Dispatch bot! This sample demonstrates using several LUIS applications and QnA Maker knowledge base using dispatch. QnAMaker.tsv What should I call you? I am the LUIS-QnAMaker Dispatch bot! This sample demonstrates using several LUIS applications and QnA Maker knowledge base using dispatch. QnAMaker.tsv ================================================ FILE: SDKV4-Samples/dotnet_core/nlp-with-dispatch/home-automation.json ================================================ { "name": "Home Automation", "versionId": "0.1", "desc": "Home Automation LUIS application - Bot Builder Samples", "culture": "en-us", "intents": [ { "name": "HomeAutomation" }, { "name": "None" } ], "entities": [ { "name": "Device", "roles": [] }, { "name": "deviceProperty", "roles": [] }, { "name": "Room", "roles": [] } ], "closedLists": [ { "name": "Operation", "subLists": [ { "canonicalForm": "off", "list": [ "off", "turn off", "switch off", "lock", "out", "shut down", "stop" ] }, { "canonicalForm": "on", "list": [ "on", "turn on", "switch on", "unlock", "un lock", "boot up", "start" ] } ], "roles": [] } ], "composites": [], "patternAnyEntities": [ { "name": "Device_PatternAny", "explicitList": [], "roles": [] }, { "name": "Room_PatternAny", "explicitList": [], "roles": [] } ], "regex_entities": [], "prebuiltEntities": [ { "name": "number", "roles": [] } ], "regex_features": [], "model_features": [], "patterns": [ { "pattern": "turn off {Device_PatternAny} in {Room_PatternAny}", "intent": "HomeAutomation" }, { "pattern": "{Device_PatternAny} off [please]", "intent": "HomeAutomation" }, { "pattern": "turn on {Device_PatternAny}", "intent": "HomeAutomation" }, { "pattern": "{Device_PatternAny} on [please]", "intent": "HomeAutomation" }, { "pattern": "turn on {Device_PatternAny} in {Room_PatternAny}", "intent": "HomeAutomation" }, { "pattern": "{Device_PatternAny} in {Room_PatternAny} on [please]", "intent": "HomeAutomation" }, { "pattern": "{Device_PatternAny} in {Room_PatternAny} off [please]", "intent": "HomeAutomation" } ], "utterances": [ { "text": "breezeway on please", "intent": "HomeAutomation", "entities": [ { "startPos": 0, "endPos": 8, "entity": "Room" } ] }, { "text": "change temperature to seventy two degrees", "intent": "HomeAutomation", "entities": [ { "startPos": 7, "endPos": 17, "entity": "deviceProperty" } ] }, { "text": "coffee bar on please", "intent": "HomeAutomation", "entities": [ { "startPos": 0, "endPos": 9, "entity": "Device" } ] }, { "text": "decrease temperature for me please", "intent": "None", "entities": [ { "startPos": 9, "endPos": 19, "entity": "deviceProperty" } ] }, { "text": "dim kitchen lights to 25 .", "intent": "None", "entities": [ { "startPos": 4, "endPos": 10, "entity": "Room" }, { "startPos": 12, "endPos": 17, "entity": "Device" } ] }, { "text": "fish pond off please", "intent": "HomeAutomation", "entities": [ { "startPos": 0, "endPos": 8, "entity": "Device" } ] }, { "text": "fish pond on please", "intent": "HomeAutomation", "entities": [ { "startPos": 0, "endPos": 8, "entity": "Device" } ] }, { "text": "illuminate please", "intent": "HomeAutomation", "entities": [ { "startPos": 0, "endPos": 9, "entity": "Device" } ] }, { "text": "living room lamp on please", "intent": "HomeAutomation", "entities": [ { "startPos": 0, "endPos": 10, "entity": "Room" }, { "startPos": 12, "endPos": 15, "entity": "Device" } ] }, { "text": "living room lamps off please", "intent": "HomeAutomation", "entities": [ { "startPos": 0, "endPos": 10, "entity": "Room" }, { "startPos": 12, "endPos": 16, "entity": "Device" } ] }, { "text": "lock the doors for me please", "intent": "HomeAutomation", "entities": [ { "startPos": 9, "endPos": 13, "entity": "Device" } ] }, { "text": "lower your volume", "intent": "None", "entities": [ { "startPos": 11, "endPos": 16, "entity": "deviceProperty" } ] }, { "text": "make camera 1 off please", "intent": "HomeAutomation", "entities": [ { "startPos": 5, "endPos": 12, "entity": "Device" } ] }, { "text": "make some coffee", "intent": "HomeAutomation", "entities": [ { "startPos": 10, "endPos": 15, "entity": "Device" } ] }, { "text": "play dvd", "intent": "HomeAutomation", "entities": [ { "startPos": 5, "endPos": 7, "entity": "Device" } ] }, { "text": "set lights out in bedroom", "intent": "HomeAutomation", "entities": [ { "startPos": 4, "endPos": 9, "entity": "Device" }, { "startPos": 18, "endPos": 24, "entity": "Room" } ] }, { "text": "set lights to bright", "intent": "HomeAutomation", "entities": [ { "startPos": 4, "endPos": 9, "entity": "Device" }, { "startPos": 14, "endPos": 19, "entity": "deviceProperty" } ] }, { "text": "set lights to concentrate", "intent": "HomeAutomation", "entities": [ { "startPos": 4, "endPos": 9, "entity": "Device" }, { "startPos": 14, "endPos": 24, "entity": "deviceProperty" } ] }, { "text": "shut down my work computer", "intent": "HomeAutomation", "entities": [ { "startPos": 18, "endPos": 25, "entity": "Device" } ] }, { "text": "snap switch fan fifty percent", "intent": "HomeAutomation", "entities": [ { "startPos": 12, "endPos": 14, "entity": "Device" } ] }, { "text": "start master bedroom light.", "intent": "HomeAutomation", "entities": [ { "startPos": 6, "endPos": 19, "entity": "Room" }, { "startPos": 21, "endPos": 25, "entity": "Device" } ] }, { "text": "theater on please", "intent": "HomeAutomation", "entities": [ { "startPos": 0, "endPos": 6, "entity": "Room" } ] }, { "text": "turn dimmer off", "intent": "HomeAutomation", "entities": [ { "startPos": 5, "endPos": 10, "entity": "Device" } ] }, { "text": "turn off ac please", "intent": "HomeAutomation", "entities": [ { "startPos": 9, "endPos": 10, "entity": "Device" } ] }, { "text": "turn off foyer lights", "intent": "HomeAutomation", "entities": [ { "startPos": 9, "endPos": 13, "entity": "Room" }, { "startPos": 15, "endPos": 20, "entity": "Device" } ] }, { "text": "turn off living room light", "intent": "HomeAutomation", "entities": [ { "startPos": 9, "endPos": 19, "entity": "Room" }, { "startPos": 21, "endPos": 25, "entity": "Device" } ] }, { "text": "turn off staircase", "intent": "HomeAutomation", "entities": [ { "startPos": 9, "endPos": 17, "entity": "Device" } ] }, { "text": "turn off venice lamp", "intent": "HomeAutomation", "entities": [ { "startPos": 16, "endPos": 19, "entity": "Device" } ] }, { "text": "turn on bathroom heater", "intent": "HomeAutomation", "entities": [ { "startPos": 8, "endPos": 15, "entity": "Room" }, { "startPos": 17, "endPos": 22, "entity": "Device" } ] }, { "text": "turn on external speaker", "intent": "HomeAutomation", "entities": [ { "startPos": 8, "endPos": 23, "entity": "Device" } ] }, { "text": "turn on kitchen faucet", "intent": "HomeAutomation", "entities": [ { "startPos": 8, "endPos": 14, "entity": "Room" }, { "startPos": 16, "endPos": 21, "entity": "Device" } ] }, { "text": "turn on light in bedroom", "intent": "HomeAutomation", "entities": [ { "startPos": 8, "endPos": 12, "entity": "Device" }, { "startPos": 17, "endPos": 23, "entity": "Room" } ] }, { "text": "turn on my bedroom lights.", "intent": "HomeAutomation", "entities": [ { "startPos": 11, "endPos": 17, "entity": "Room" }, { "startPos": 19, "endPos": 24, "entity": "Device" } ] }, { "text": "turn on the furnace room lights", "intent": "HomeAutomation", "entities": [ { "startPos": 12, "endPos": 23, "entity": "Room" }, { "startPos": 25, "endPos": 30, "entity": "Device" } ] }, { "text": "turn on the internet in my bedroom please", "intent": "None", "entities": [ { "startPos": 27, "endPos": 33, "entity": "Room" } ] }, { "text": "turn on thermostat please", "intent": "HomeAutomation", "entities": [ { "startPos": 8, "endPos": 17, "entity": "Device" } ] }, { "text": "turn the fan to high", "intent": "HomeAutomation", "entities": [ { "startPos": 9, "endPos": 11, "entity": "Device" } ] }, { "text": "turn thermostat on 70.", "intent": "HomeAutomation", "entities": [ { "startPos": 5, "endPos": 14, "entity": "Device" } ] } ] } ================================================ FILE: SDKV4-Samples/dotnet_core/nlp-with-dispatch/nlp-with-dispatchDispatch.json ================================================ { "name": "nlp-with-dispatchDispatch", "versionId": "Dispatch", "desc": "Dispatch model for nlp-with-dispatchDispatch", "culture": "en-us", "intents": [ { "name": "l_Home Automation" }, { "name": "l_Weather" }, { "name": "None" }, { "name": "q_sample-qna" } ], "entities": [], "closedLists": [], "composites": [], "patternAnyEntities": [], "regex_entities": [], "prebuiltEntities": [], "regex_features": [], "model_features": [], "patterns": [], "utterances": [ { "text": "breezeway on please", "intent": "l_Home Automation", "entities": [] }, { "text": "change temperature to seventy two degrees", "intent": "l_Home Automation", "entities": [] }, { "text": "coffee bar on please", "intent": "l_Home Automation", "entities": [] }, { "text": "current weather ?", "intent": "l_Weather", "entities": [] }, { "text": "decrease temperature for me please", "intent": "None", "entities": [] }, { "text": "dim kitchen lights to 25 .", "intent": "None", "entities": [] }, { "text": "do florida residents usually need ice scrapers", "intent": "l_Weather", "entities": [] }, { "text": "fish pond off please", "intent": "l_Home Automation", "entities": [] }, { "text": "fish pond on please", "intent": "l_Home Automation", "entities": [] }, { "text": "forecast in celcius", "intent": "l_Weather", "entities": [] }, { "text": "get florence temperature in september", "intent": "l_Weather", "entities": [] }, { "text": "get for me the weather conditions in sonoma county", "intent": "l_Weather", "entities": [] }, { "text": "get the daily temperature greenwood indiana", "intent": "l_Weather", "entities": [] }, { "text": "get the forcast for me", "intent": "l_Weather", "entities": [] }, { "text": "get the weather at saint george utah", "intent": "l_Weather", "entities": [] }, { "text": "good evening", "intent": "q_sample-qna", "entities": [] }, { "text": "good morning", "intent": "q_sample-qna", "entities": [] }, { "text": "greetings", "intent": "q_sample-qna", "entities": [] }, { "text": "hi", "intent": "q_sample-qna", "entities": [] }, { "text": "how much rain does chambersburg get a year", "intent": "l_Weather", "entities": [] }, { "text": "i want to know the temperature at death valley", "intent": "l_Weather", "entities": [] }, { "text": "illuminate please", "intent": "l_Home Automation", "entities": [] }, { "text": "living room lamp on please", "intent": "l_Home Automation", "entities": [] }, { "text": "living room lamps off please", "intent": "l_Home Automation", "entities": [] }, { "text": "lock the doors for me please", "intent": "l_Home Automation", "entities": [] }, { "text": "lower your volume", "intent": "None", "entities": [] }, { "text": "make camera 1 off please", "intent": "l_Home Automation", "entities": [] }, { "text": "make some coffee", "intent": "l_Home Automation", "entities": [] }, { "text": "play dvd", "intent": "l_Home Automation", "entities": [] }, { "text": "provide me by toronto weather please", "intent": "l_Weather", "entities": [] }, { "text": "set lights out in bedroom", "intent": "l_Home Automation", "entities": [] }, { "text": "set lights to bright", "intent": "l_Home Automation", "entities": [] }, { "text": "set lights to concentrate", "intent": "l_Home Automation", "entities": [] }, { "text": "show average rainfall for boise", "intent": "l_Weather", "entities": [] }, { "text": "show me the forecast at alabama", "intent": "l_Weather", "entities": [] }, { "text": "shut down my work computer", "intent": "l_Home Automation", "entities": [] }, { "text": "snap switch fan fifty percent", "intent": "l_Home Automation", "entities": [] }, { "text": "soliciting today's weather", "intent": "l_Weather", "entities": [] }, { "text": "start master bedroom light.", "intent": "l_Home Automation", "entities": [] }, { "text": "temperature of delhi in celsius please", "intent": "l_Weather", "entities": [] }, { "text": "theater on please", "intent": "l_Home Automation", "entities": [] }, { "text": "turn dimmer off", "intent": "l_Home Automation", "entities": [] }, { "text": "turn off ac please", "intent": "l_Home Automation", "entities": [] }, { "text": "turn off foyer lights", "intent": "l_Home Automation", "entities": [] }, { "text": "turn off living room light", "intent": "l_Home Automation", "entities": [] }, { "text": "turn off staircase", "intent": "l_Home Automation", "entities": [] }, { "text": "turn off venice lamp", "intent": "l_Home Automation", "entities": [] }, { "text": "turn on bathroom heater", "intent": "l_Home Automation", "entities": [] }, { "text": "turn on external speaker", "intent": "l_Home Automation", "entities": [] }, { "text": "turn on kitchen faucet", "intent": "l_Home Automation", "entities": [] }, { "text": "turn on light in bedroom", "intent": "l_Home Automation", "entities": [] }, { "text": "turn on my bedroom lights.", "intent": "l_Home Automation", "entities": [] }, { "text": "turn on the furnace room lights", "intent": "l_Home Automation", "entities": [] }, { "text": "turn on the internet in my bedroom please", "intent": "None", "entities": [] }, { "text": "turn on thermostat please", "intent": "l_Home Automation", "entities": [] }, { "text": "turn the fan to high", "intent": "l_Home Automation", "entities": [] }, { "text": "turn thermostat on 70.", "intent": "l_Home Automation", "entities": [] }, { "text": "was last year about this time as wet as it is now in the south ?", "intent": "l_Weather", "entities": [] }, { "text": "what are you?", "intent": "q_sample-qna", "entities": [] }, { "text": "what do you do?", "intent": "q_sample-qna", "entities": [] }, { "text": "what is the rain volume in sonoma county ?", "intent": "l_Weather", "entities": [] }, { "text": "what is the weather in redmond ?", "intent": "l_Weather", "entities": [] }, { "text": "what is the weather today at 10 day durham ?", "intent": "l_Weather", "entities": [] }, { "text": "what is your name?", "intent": "q_sample-qna", "entities": [] }, { "text": "what should i call you?", "intent": "q_sample-qna", "entities": [] }, { "text": "what to wear in march in california", "intent": "l_Weather", "entities": [] }, { "text": "what will the weather be tomorrow in new york ?", "intent": "l_Weather", "entities": [] }, { "text": "what?", "intent": "q_sample-qna", "entities": [] }, { "text": "what's the weather going to be like in hawaii ?", "intent": "l_Weather", "entities": [] }, { "text": "what's the weather like in minneapolis", "intent": "l_Weather", "entities": [] }, { "text": "who are you?", "intent": "q_sample-qna", "entities": [] }, { "text": "will it be raining in ranchi", "intent": "l_Weather", "entities": [] }, { "text": "will it rain this weekend", "intent": "l_Weather", "entities": [] }, { "text": "will it snow today", "intent": "l_Weather", "entities": [] } ] } ================================================ FILE: SDKV4-Samples/dotnet_core/nlp-with-dispatch/weather.json ================================================ { "name": "Weather", "versionId": "0.1", "desc": "Weather LUIS application - Bot Builder Samples", "culture": "en-us", "intents": [ { "name": "Get Weather Condition" }, { "name": "Get Weather Forecast" }, { "name": "None" } ], "entities": [ { "name": "Location", "roles": [] } ], "closedLists": [], "composites": [], "patternAnyEntities": [ { "name": "Location_PatternAny", "explicitList": [], "roles": [] } ], "regex_entities": [], "prebuiltEntities": [], "regex_features": [], "model_features": [], "patterns": [ { "pattern": "weather in {Location_PatternAny}", "intent": "Get Weather Condition" }, { "pattern": "how's the weather in {Location_PatternAny}", "intent": "Get Weather Condition" }, { "pattern": "current weather in {Location_PatternAny}", "intent": "Get Weather Condition" }, { "pattern": "what's the forecast for next week in {Location_PatternAny}", "intent": "Get Weather Forecast" }, { "pattern": "show me the forecast for {Location_PatternAny}", "intent": "Get Weather Forecast" }, { "pattern": "what's the forecast for {Location_PatternAny}", "intent": "Get Weather Forecast" } ], "utterances": [ { "text": "current weather ?", "intent": "Get Weather Condition", "entities": [] }, { "text": "do florida residents usually need ice scrapers", "intent": "Get Weather Condition", "entities": [ { "startPos": 3, "endPos": 9, "entity": "Location" } ] }, { "text": "forecast in celcius", "intent": "Get Weather Forecast", "entities": [] }, { "text": "get florence temperature in september", "intent": "Get Weather Condition", "entities": [ { "startPos": 4, "endPos": 11, "entity": "Location" } ] }, { "text": "get for me the weather conditions in sonoma county", "intent": "Get Weather Condition", "entities": [ { "startPos": 37, "endPos": 49, "entity": "Location" } ] }, { "text": "get the daily temperature greenwood indiana", "intent": "Get Weather Condition", "entities": [ { "startPos": 26, "endPos": 42, "entity": "Location" } ] }, { "text": "get the forcast for me", "intent": "Get Weather Forecast", "entities": [] }, { "text": "get the weather at saint george utah", "intent": "Get Weather Condition", "entities": [ { "startPos": 19, "endPos": 35, "entity": "Location" } ] }, { "text": "how much rain does chambersburg get a year", "intent": "Get Weather Condition", "entities": [ { "startPos": 19, "endPos": 30, "entity": "Location" } ] }, { "text": "i want to know the temperature at death valley", "intent": "Get Weather Forecast", "entities": [ { "startPos": 34, "endPos": 45, "entity": "Location" } ] }, { "text": "provide me by toronto weather please", "intent": "Get Weather Forecast", "entities": [ { "startPos": 14, "endPos": 20, "entity": "Location" } ] }, { "text": "show average rainfall for boise", "intent": "Get Weather Condition", "entities": [ { "startPos": 26, "endPos": 30, "entity": "Location" } ] }, { "text": "show me the forecast at alabama", "intent": "Get Weather Forecast", "entities": [ { "startPos": 24, "endPos": 30, "entity": "Location" } ] }, { "text": "soliciting today's weather", "intent": "Get Weather Forecast", "entities": [] }, { "text": "temperature of delhi in celsius please", "intent": "Get Weather Condition", "entities": [ { "startPos": 15, "endPos": 19, "entity": "Location" } ] }, { "text": "was last year about this time as wet as it is now in the south ?", "intent": "Get Weather Condition", "entities": [ { "startPos": 57, "endPos": 61, "entity": "Location" } ] }, { "text": "what is the rain volume in sonoma county ?", "intent": "Get Weather Condition", "entities": [ { "startPos": 27, "endPos": 39, "entity": "Location" } ] }, { "text": "what is the weather in redmond ?", "intent": "Get Weather Forecast", "entities": [ { "startPos": 23, "endPos": 29, "entity": "Location" } ] }, { "text": "what is the weather today at 10 day durham ?", "intent": "Get Weather Forecast", "entities": [ { "startPos": 36, "endPos": 41, "entity": "Location" } ] }, { "text": "what to wear in march in california", "intent": "Get Weather Condition", "entities": [ { "startPos": 25, "endPos": 34, "entity": "Location" } ] }, { "text": "what will the weather be tomorrow in new york ?", "intent": "Get Weather Forecast", "entities": [ { "startPos": 37, "endPos": 44, "entity": "Location" } ] }, { "text": "what's the weather going to be like in hawaii ?", "intent": "Get Weather Forecast", "entities": [ { "startPos": 39, "endPos": 44, "entity": "Location" } ] }, { "text": "what's the weather like in minneapolis", "intent": "Get Weather Condition", "entities": [ { "startPos": 27, "endPos": 37, "entity": "Location" } ] }, { "text": "will it be raining in ranchi", "intent": "Get Weather Forecast", "entities": [ { "startPos": 22, "endPos": 27, "entity": "Location" } ] }, { "text": "will it rain this weekend", "intent": "Get Weather Forecast", "entities": [] }, { "text": "will it snow today", "intent": "Get Weather Forecast", "entities": [] } ] } ================================================ FILE: SDKV4-Samples/dotnet_core/nlp-with-luis/reminders-with-entities.json ================================================ { "intents": [ { "name": "Calendar_Add" }, { "name": "Calendar_Find" }, { "name": "None" } ], "entities": [ { "name": "Appointment", "roles": [] }, { "name": "Meeting", "roles": [] }, { "name": "Schedule", "roles": [] } ], "composites": [], "closedLists": [], "patternAnyEntities": [], "regex_entities": [], "prebuiltEntities": [], "model_features": [], "regex_features": [], "patterns": [], "utterances": [ { "text": "add a new event on 27 - apr", "intent": "Calendar_Add", "entities": [] }, { "text": "add a new task finish assignment", "intent": "Calendar_Add", "entities": [] }, { "text": "add an event to read about adam lambert news", "intent": "Calendar_Add", "entities": [] }, { "text": "add an event to visit 209 nashville gym", "intent": "Calendar_Add", "entities": [] }, { "text": "add date to my schedule", "intent": "Calendar_Add", "entities": [ { "entity": "Schedule", "startPos": 15, "endPos": 22 } ] }, { "text": "add imax theater to my upcoming events", "intent": "Calendar_Add", "entities": [] }, { "text": "am i free to be with friends saturday ?", "intent": "None", "entities": [] }, { "text": "appointment with johnson needs to be next week", "intent": "None", "entities": [ { "entity": "Appointment", "startPos": 0, "endPos": 10 } ] }, { "text": "calendar for november 1948", "intent": "Calendar_Find", "entities": [] }, { "text": "calendar i ' ll be at the garage from 8 till 3 this saturday", "intent": "Calendar_Add", "entities": [] }, { "text": "call dad mike", "intent": "None", "entities": [] }, { "text": "change the meeting with chris to 9 : 00 am", "intent": "None", "entities": [] }, { "text": "display weekend plans", "intent": "Calendar_Find", "entities": [] }, { "text": "do i have anything on wednesday ?", "intent": "Calendar_Find", "entities": [] }, { "text": "dunmore pa sonic sounds friday morning please", "intent": "Calendar_Add", "entities": [] }, { "text": "email cloney john", "intent": "None", "entities": [] }, { "text": "extend lunch meeting 30 minutes extra", "intent": "None", "entities": [ { "entity": "Meeting", "startPos": 13, "endPos": 19 } ] }, { "text": "how many days are there between march 13th 2015 and today ?", "intent": "Calendar_Find", "entities": [] }, { "text": "i want to reschedule the meeting at the air force club", "intent": "None", "entities": [] }, { "text": "marketing meetings on tuesdays will now be every wednesday please change on my calendar", "intent": "None", "entities": [] }, { "text": "meet my manager", "intent": "Calendar_Add", "entities": [] }, { "text": "meeting my manager", "intent": "Calendar_Add", "entities": [] }, { "text": "move the bbq party to friday", "intent": "None", "entities": [] }, { "text": "pull up my appointment find out how much time i have before my next appointment", "intent": "Calendar_Find", "entities": [ { "entity": "Appointment", "startPos": 11, "endPos": 21 } ] }, { "text": "save the date may 17 pictures party", "intent": "Calendar_Add", "entities": [] }, { "text": "schedule a conference call for tomorrow", "intent": "Calendar_Add", "entities": [ { "entity": "Schedule", "startPos": 0, "endPos": 7 } ] }, { "text": "schedule a meeting for tomorrow", "intent": "Calendar_Add", "entities": [ { "entity": "Meeting", "startPos": 11, "endPos": 17 } ] }, { "text": "schedule appointment for tomorrow please", "intent": "Calendar_Add", "entities": [ { "entity": "Schedule", "startPos": 0, "endPos": 7 }, { "entity": "Appointment", "startPos": 9, "endPos": 19 } ] }, { "text": "search for meetings with chris", "intent": "Calendar_Find", "entities": [ { "entity": "Meeting", "startPos": 11, "endPos": 18 } ] }, { "text": "show me tomorrow ' s wedding party time", "intent": "Calendar_Find", "entities": [] }, { "text": "show my schedule for tomorrow", "intent": "Calendar_Find", "entities": [ { "entity": "Schedule", "startPos": 8, "endPos": 15 } ] }, { "text": "tell me the event details", "intent": "Calendar_Find", "entities": [] }, { "text": "the meeting will last for one hour", "intent": "Calendar_Add", "entities": [ { "entity": "Meeting", "startPos": 4, "endPos": 10 } ] }, { "text": "the workshop will last for 10 hours", "intent": "None", "entities": [] }, { "text": "voice activated reading of appointments this week", "intent": "Calendar_Find", "entities": [ { "entity": "Appointment", "startPos": 27, "endPos": 38 } ] } ], "settings": [] } ================================================ FILE: SDKV4-Samples/js/DialogPromptBot/.eslintrc.js ================================================ module.exports = { "extends": "standard", "rules": { "semi": [2, "always"], "indent": [2, 4], "no-return-await": 0, "space-before-function-paren": [2, { "named": "never", "anonymous": "never", "asyncArrow": "always" }], "template-curly-spacing": [2, "always"] } }; ================================================ FILE: SDKV4-Samples/js/DialogPromptBot/.gitignore ================================================ node_modules .env ================================================ FILE: SDKV4-Samples/js/DialogPromptBot/README.md ================================================ # Dialog Prompt Bot Bot from the prompt-users-using-dialogs topic This bot has been created using [Microsoft Bot Framework][10], it shows how to create a simple echo bot with state. The bot maintains a simple counter that increases with each message from the user. This bot example uses [`restify`][1]. # To run the bot - Install modules and start the bot ```bash npm i & npm start ``` Alternatively you can also use nodemon via ```bash npm i & npm run watch ``` # Testing the bot using Bot Framework Emulator [Microsoft Bot Framework Emulator][2] is a desktop application that allows bot developers to test and debug their bots on localhost or running remotely through a tunnel. - Install the Bot Framework emulator from [here][3] ## Connect to bot using Bot Framework Emulator **V4** - Launch Bot Framework Emulator - File -> Open Bot Configuration - Select `dialog-prompt.bot` file # Bot state A key to good bot design is to track the context of a conversation, so that your bot remembers things like the answers to previous questions. Depending on what your bot is used for, you may even need to keep track of conversation state or store user related information for longer than the lifetime of one given conversation. In this example, the bot's state is used to track number of messages. A bot's state is information it remembers in order to respond appropriately to incoming messages. The Bot Builder SDK provides classes for [storing and retrieving state data][4] as an object associated with a user or a conversation. - Conversation properties help your bot keep track of the current conversation the bot is having with the user. If your bot needs to complete a sequence of steps or switch between conversation topics, you can use conversation properties to manage steps in a sequence or track the current topic. Since conversation properties reflect the state of the current conversation, you typically clear them at the end of a session, when the bot receives an end of conversation activity. - User properties can be used for many purposes, such as determining where the user's prior conversation left off or simply greeting a returning user by name. If you store a user's preferences, you can use that information to customize the conversation the next time you chat. For example, you might alert the user to a news article about a topic that interests her, or alert a user when an appointment becomes available. You should clear them if the bot receives a delete user data activity. # Deploy this bot to Azure You can use the [MSBot][5] Bot Builder CLI tool to clone and configure the services this sample depends on. To install all Bot Builder tools - Ensure you have [Node.js](https://nodejs.org/) version 8.5 or higher ```bash npm i -g msbot chatdown ludown qnamaker luis-apis botdispatch luisgen ``` To clone this bot, run ``` msbot clone services -f deploymentScripts/msbotClone -n myChatBot -l --subscriptionId ``` # Further reading - [Azure Bot Service Introduction][6] - [Bot State][7] - [Write directly to storage][8] - [Managing conversation and user state][9] [1]: https://www.npmjs.com/package/restify [2]: https://github.com/microsoft/botframework-emulator [3]: https://aka.ms/botframework-emulator [4]: https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-howto-v4-state?view=azure-bot-service-4.0&tabs=js [5]: https://github.com/microsoft/botbuilder-tools [6]: https://docs.microsoft.com/en-us/azure/bot-service/bot-service-overview-introduction?view=azure-bot-service-4.0 [7]: https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-storage-concept?view=azure-bot-service-4.0 [8]: https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-howto-v4-storage?view=azure-bot-service-4.0&tabs=jsechoproperty%2Ccsetagoverwrite%2Ccsetag [9]: https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-howto-v4-state?view=azure-bot-service-4.0&tabs=js [10] https://dev.botframework.com ================================================ FILE: SDKV4-Samples/js/DialogPromptBot/bot.js ================================================ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. const { ActivityTypes } = require('botbuilder'); const { DialogSet, WaterfallDialog, NumberPrompt, DateTimePrompt, ChoicePrompt, DialogTurnStatus } = require('botbuilder-dialogs'); // Define identifiers for our state property accessors. const DIALOG_STATE_ACCESSOR = 'dialogStateAccessor'; const RESERVATION_ACCESSOR = 'reservationAccessor'; // Define identifiers for our dialogs and prompts. const RESERVATION_DIALOG = 'reservationDialog'; const SIZE_RANGE_PROMPT = 'rangePrompt'; const LOCATION_PROMPT = 'locationPrompt'; const RESERVATION_DATE_PROMPT = 'reservationDatePrompt'; class DialogPromptBot { /** * * @param {ConversationState} conversation state object */ constructor(conversationState) { // Creates our state accessor properties. // See https://aka.ms/about-bot-state-accessors to learn more about the bot state and state accessors. this.dialogStateAccessor = conversationState.createProperty(DIALOG_STATE_ACCESSOR); this.reservationAccessor = conversationState.createProperty(RESERVATION_ACCESSOR); this.conversationState = conversationState; // Create the dialog set and add the prompts, including custom validation. this.dialogSet = new DialogSet(this.dialogStateAccessor); this.dialogSet.add(new NumberPrompt(SIZE_RANGE_PROMPT, this.rangeValidator)); this.dialogSet.add(new ChoicePrompt(LOCATION_PROMPT)); this.dialogSet.add(new DateTimePrompt(RESERVATION_DATE_PROMPT, this.dateValidator)); // Define the steps of the waterfall dialog and add it to the set. this.dialogSet.add(new WaterfallDialog(RESERVATION_DIALOG, [ this.promptForPartySize.bind(this), this.promptForLocation.bind(this), this.promptForReservationDate.bind(this), this.acknowledgeReservation.bind(this), ])); } /** * * @param {TurnContext} on turn context object. */ async onTurn(turnContext) { switch (turnContext.activity.type) { case ActivityTypes.Message: // Get the current reservation info from state. const reservation = await this.reservationAccessor.get(turnContext, null); // Generate a dialog context for our dialog set. const dc = await this.dialogSet.createContext(turnContext); if (!dc.activeDialog) { // If there is no active dialog, check whether we have a reservation yet. if (!reservation) { // If not, start the dialog. await dc.beginDialog(RESERVATION_DIALOG); } else { // Otherwise, send a status message. await turnContext.sendActivity( `We'll see you on ${reservation.date}.`); } } else { // Continue the dialog. const dialogTurnResult = await dc.continueDialog(); // If the dialog completed this turn, record the reservation info. if (dialogTurnResult.status === DialogTurnStatus.complete) { await this.reservationAccessor.set( turnContext, dialogTurnResult.result); // Send a confirmation message to the user. await turnContext.sendActivity( `Your party of ${dialogTurnResult.result.size} is ` + `confirmed for ${dialogTurnResult.result.date} in ` + `${dialogTurnResult.result.location}.`); } } // Save the updated dialog state into the conversation state. await this.conversationState.saveChanges(turnContext, false); break; case ActivityTypes.EndOfConversation: case ActivityTypes.DeleteUserData: break; default: break; } } async promptForPartySize(stepContext) { // Prompt for the party size. The result of the prompt is returned to the next step of the waterfall. return await stepContext.prompt( SIZE_RANGE_PROMPT, { prompt: 'How many people is the reservation for?', retryPrompt: 'How large is your party?', validations: { min: 3, max: 8 }, }); } async promptForLocation(stepContext) { // Record the party size information in the current dialog state. stepContext.values.size = stepContext.result; // Prompt for location. return await stepContext.prompt(LOCATION_PROMPT, { prompt: 'Please choose a location.', retryPrompt: 'Sorry, please choose a location from the list.', choices: ['Redmond', 'Bellevue', 'Seattle'], }); } async promptForReservationDate(stepContext) { // Record the location information in the current dialog state. stepContext.values.location = stepContext.result.value; return await stepContext.prompt( RESERVATION_DATE_PROMPT, { prompt: 'Great. When will the reservation be for?', retryPrompt: 'What time should we make your reservation for?' }); } async acknowledgeReservation(stepContext) { // Retrieve the reservation date. const resolution = stepContext.result[0]; const time = resolution.value || resolution.start; // Send an acknowledgement to the user. await stepContext.context.sendActivity( 'Thank you. We will confirm your reservation shortly.'); // Return the collected information to the parent context. return await stepContext.endDialog({ date: time, size: stepContext.values.size, location: stepContext.values.location }); } async rangeValidator(promptContext) { // Check whether the input could be recognized as an integer. if (!promptContext.recognized.succeeded) { await promptContext.context.sendActivity( "I'm sorry, I do not understand. Please enter the number of people in your party."); return false; } else if (promptContext.recognized.value % 1 != 0) { await promptContext.context.sendActivity( "I'm sorry, I don't understand fractional people."); return false; } // Check whether the party size is appropriate. var size = promptContext.recognized.value; if (size < promptContext.options.validations.min || size > promptContext.options.validations.max) { await promptContext.context.sendActivity( 'Sorry, we can only take reservations for parties of ' + `${promptContext.options.validations.min} to ` + `${promptContext.options.validations.max}.`); await promptContext.context.sendActivity(promptContext.options.retryPrompt); return false; } return true; } async dateValidator(promptContext) { // Check whether the input could be recognized as an integer. if (!promptContext.recognized.succeeded) { await promptContext.context.sendActivity( "I'm sorry, I do not understand. Please enter the date or time for your reservation."); return false; } // Check whether any of the recognized date-times are appropriate, // and if so, return the first appropriate date-time. const earliest = Date.now() + (60 * 60 * 1000); let value = null; promptContext.recognized.value.forEach(candidate => { // TODO: update validation to account for time vs date vs date-time vs range. const time = new Date(candidate.value || candidate.start); if (earliest < time.getTime()) { value = candidate; } }); if (value) { promptContext.recognized.value = [value]; return true; } await promptContext.context.sendActivity( "I'm sorry, we can't take reservations earlier than an hour from now."); return false; } } module.exports.DialogPromptBot = DialogPromptBot; ================================================ FILE: SDKV4-Samples/js/DialogPromptBot/deploymentScripts/msbotClone/bot.recipe ================================================ { "version": "1.0", "resources": [ { "type": "endpoint", "id": "1", "name": "development", "url": "http://localhost:3978/api/messages" }, { "type": "endpoint", "id": "2", "name": "production", "url": "https://your-bot-url.azurewebsites.net/api/messages" }, { "type": "abs", "id": "3", "name": "DialogPromptBot-abs" }, { "type": "appInsights", "id": "4", "name": "DialogPromptBot-insights" }, { "type": "blob", "id": "5", "name": "DialogPromptBot-blob", "container": "botstatestore" } ] } ================================================ FILE: SDKV4-Samples/js/DialogPromptBot/dialog-prompt.bot ================================================ { "name": "DialogPromptBot", "services": [ { "type": "endpoint", "name": "development", "endpoint": "http://localhost:3978/api/messages", "appId": "", "appPassword": "", "id": "1" } ], "padlock": "", "version": "2.0" } ================================================ FILE: SDKV4-Samples/js/DialogPromptBot/index.js ================================================ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. const path = require('path'); const restify = require('restify'); // Import required bot services. // See https://aka.ms/bot-services to learn more about the different parts of a bot. const { BotFrameworkAdapter, MemoryStorage, ConversationState } = require('botbuilder'); // Import required bot configuration. const { BotConfiguration } = require('botframework-config'); // This bot's main dialog. const { DialogPromptBot } = require('./bot'); // Read botFilePath and botFileSecret from .env file // Note: Ensure you have a .env file and include botFilePath and botFileSecret. const ENV_FILE = path.join(__dirname, '.env'); const env = require('dotenv').config({path: ENV_FILE}); // bot endpoint name as defined in .bot file // See https://aka.ms/about-bot-file to learn more about .bot file its use and bot configuration . const DEV_ENVIRONMENT = 'development'; // bot name as defined in .bot file // See https://aka.ms/about-bot-file to learn more about .bot file its use and bot configuration. const BOT_CONFIGURATION = (process.env.NODE_ENV || DEV_ENVIRONMENT); // Create HTTP server let server = restify.createServer(); server.listen(process.env.port || process.env.PORT || 3978, function () { console.log(`\n${server.name} listening to ${server.url}`); console.log(`\nGet Bot Framework Emulator: https://aka.ms/botframework-emulator`); console.log(`\nTo talk to your bot, open dialog-prompt.bot file in the Emulator`); }); // .bot file path const BOT_FILE = path.join(__dirname, (process.env.botFilePath || '')); // Read bot configuration from .bot file. let botConfig; try { botConfig = BotConfiguration.loadSync(BOT_FILE, process.env.botFileSecret); } catch (err) { console.error(`\nError reading bot file. Please ensure you have valid botFilePath and botFileSecret set for your environment.`); console.error(`\n - The botFileSecret is available under appsettings for your Azure Bot Service bot.`); console.error(`\n - If you are running this bot locally, consider adding a .env file with botFilePath and botFileSecret.\n\n`); process.exit(); } // Get bot endpoint configuration by service name const endpointConfig = botConfig.findServiceByNameOrId(BOT_CONFIGURATION); // Create adapter. // See https://aka.ms/about-bot-adapter to learn more about .bot file its use and bot configuration . const adapter = new BotFrameworkAdapter({ appId: endpointConfig.appId || process.env.microsoftAppID, appPassword: endpointConfig.appPassword || process.env.microsoftAppPassword }); // Define state store for your bot. // See https://aka.ms/about-bot-state to learn more about bot state. const memoryStorage = new MemoryStorage(); // CAUTION: You must ensure your product environment has the NODE_ENV set // to use the Azure Blob storage or Azure Cosmos DB providers. // const { BlobStorage } = require('botbuilder-azure'); // Storage configuration name or ID from .bot file // const STORAGE_CONFIGURATION_ID = ''; // // Default container name // const DEFAULT_BOT_CONTAINER = ''; // // Get service configuration // const blobStorageConfig = botConfig.findServiceByNameOrId(STORAGE_CONFIGURATION_ID); // const blobStorage = new BlobStorage({ // containerName: (blobStorageConfig.container || DEFAULT_BOT_CONTAINER), // storageAccountOrConnectionString: blobStorageConfig.connectionString, // }); // conversationState = new ConversationState(blobStorage); // Create conversation state with in-memory storage provider. const conversationState = new ConversationState(memoryStorage); // Create the main dialog. const myBot = new DialogPromptBot(conversationState); // Catch-all for errors. adapter.onTurnError = async (context, error) => { // This check writes out errors to console log .vs. app insights. console.error(`\n [onTurnError]: ${error}`); // Send a message to the user context.sendActivity(`Oops. Something went wrong!`); // Clear out state await conversationState.load(context); await conversationState.clear(context); // Save state changes. await conversationState.saveChanges(context); }; // Listen for incoming requests. server.post('/api/messages', (req, res) => { adapter.processActivity(req, res, async (context) => { // Route to main dialog. await myBot.onTurn(context); }); }); ================================================ FILE: SDKV4-Samples/js/DialogPromptBot/package.json ================================================ { "name": "dialog-prompt-bot", "version": "1.0.0", "description": "Bot from the prompt-users-using-dialogs topic", "author": "Microsoft Bot Builder Yeoman Generator v4.0.10", "license": "MIT", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build": "echo \"Error: no build specified\" && exit 1", "start": "node ./index.js", "watch": "nodemon ./index.js" }, "dependencies": { "botbuilder": "^4.2.1", "botbuilder-dialogs": "^4.2.1", "botframework-config": "^4.1.5", "dotenv": "^6.0.0", "restify": "^6.3.4" }, "devDependencies": { "eslint": "^5.6.0", "eslint-config-standard": "^12.0.0", "eslint-plugin-import": "^2.14.0", "eslint-plugin-node": "^7.0.1", "eslint-plugin-promise": "^4.0.1", "eslint-plugin-standard": "^4.0.0", "nodemon": "^1.18.4", "@types/node": "10.10.2", "@types/restify": "7.2.4" } } ================================================ FILE: SDKV4-Samples/js/PromptUsersForInput/.eslintrc.js ================================================ module.exports = { "extends": "standard", "rules": { "semi": [2, "always"], "indent": [2, 4], "no-return-await": 0, "space-before-function-paren": [2, { "named": "never", "anonymous": "never", "asyncArrow": "always" }], "template-curly-spacing": [2, "always"] } }; ================================================ FILE: SDKV4-Samples/js/PromptUsersForInput/README.md ================================================ # Prompt users for input This sample demonstrates how to create your own prompts with an ASP.Net Core 2 bot. The bot maintains conversation state to track and direct the conversation and ask the user questions. The bot maintains user state to track the user's answers. This bot example uses [`restify`][restify]. # To run the bot - Install modules and start the bot ```bash npm i npm start ``` Alternatively you can also use nodemon via ```bash npm i npm run watch ``` # Testing the bot using Bot Framework Emulator [Microsoft Bot Framework Emulator][emulator] is a desktop application that allows bot developers to test and debug their bots on localhost or running remotely through a tunnel. - Install the Bot Framework emulator from [here][emulator] ## Connect to bot using Bot Framework Emulator **V4** - Launch Bot Framework Emulator - File -> Open Bot Configuration - Select `simplePrompts.bot` file # Bot state A bot is inherently stateless. Once your bot is deployed, it may not run in the same process or on the same machine from one turn to the next. However, your bot may need to track the context of a conversation, so that it can manage its behavior and remember answers to previous questions. In this example, the bot's state is used to a track number of messages. - We use the bot's turn handler and user and conversation state properties to manage the flow of the conversation and the collection of input. - We ask the user a series of questions; parse, validate, and normalize their answers; and then save their input. This sample is intended to be run and tested locally and is not designed to be deployed to Azure. # Further reading - [Azure Bot Service introduction][bot-service-overview] - [About bot state][state-concept] - [Prompt users for input][primitive-prompts] - [Managing conversation and user state][state-how-to] - [Write directly to storage][storage-how-to] [restify]: https://www.npmjs.com/package/restify [emulator]: https://aka.ms/botframework-emulator [bot-service-docs]: https://docs.microsoft.com/azure/bot-service/ [bot-service-overview]: https://docs.microsoft.com/azure/bot-service/bot-service-overview-introduction [state-concept]: https://docs.microsoft.com/azure/bot-service/bot-builder-concept-state [primitive-prompts]: https://docs.microsoft.com/azure/bot-service/bot-builder-primitive-prompts [state-how-to]: https://docs.microsoft.com/azure/bot-service/bot-builder-howto-v4-state [storage-how-to]: https://docs.microsoft.com/azure/bot-service/bot-builder-howto-v4-storage ================================================ FILE: SDKV4-Samples/js/PromptUsersForInput/bot.js ================================================ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. const Recognizers = require('@microsoft/recognizers-text-suite'); const { ActivityTypes, MessageFactory } = require('botbuilder'); // The accessor names for the conversation flow and user profile state property accessors. const CONVERSATION_FLOW_PROPERTY = 'conversationFlowProperty'; const USER_PROFILE_PROPERTY = 'userProfileProperty'; // Identifies the last question asked. const question = { name: "name", age: "age", date: "date", none: "none" } // Defines a bot for filling a user profile. class MyBot { constructor(conversationState, userState) { // The state property accessors for conversation flow and user profile. this.conversationFlow = conversationState.createProperty(CONVERSATION_FLOW_PROPERTY); this.userProfile = userState.createProperty(USER_PROFILE_PROPERTY); // The state management objects for the conversation and user. this.conversationState = conversationState; this.userState = userState; } // The bot's turn handler. async onTurn(turnContext) { // This bot listens for message activities. if (turnContext.activity.type === ActivityTypes.Message) { // Get the state properties from the turn context. const flow = await this.conversationFlow.get(turnContext, { lastQuestionAsked: question.none }); const profile = await this.userProfile.get(turnContext, {}); await MyBot.fillOutUserProfile(flow, profile, turnContext); // Update state and save changes. await this.conversationFlow.set(turnContext, flow); await this.conversationState.saveChanges(turnContext); await this.userProfile.set(turnContext, profile); await this.userState.saveChanges(turnContext); } } // Manages the conversation flow for filling out the user's profile. static async fillOutUserProfile(flow, profile, turnContext) { const input = turnContext.activity.text; let result; switch (flow.lastQuestionAsked) { // If we're just starting off, we haven't asked the user for any information yet. // Ask the user for their name and update the conversation flag. case question.none: await turnContext.sendActivity("Let's get started. What is your name?"); flow.lastQuestionAsked = question.name; break; // If we last asked for their name, record their response, confirm that we got it. // Ask them for their age and update the conversation flag. case question.name: result = this.validateName(input); if (result.success) { profile.name = result.name; await turnContext.sendActivity(`I have your name as ${profile.name}.`); await turnContext.sendActivity('How old are you?'); flow.lastQuestionAsked = question.age; break; } else { // If we couldn't interpret their input, ask them for it again. // Don't update the conversation flag, so that we repeat this step. await turnContext.sendActivity( result.message || "I'm sorry, I didn't understand that."); break; } // If we last asked for their age, record their response, confirm that we got it. // Ask them for their date preference and update the conversation flag. case question.age: result = this.validateAge(input); if (result.success) { profile.age = result.age; await turnContext.sendActivity(`I have your age as ${profile.age}.`); await turnContext.sendActivity('When is your flight?'); flow.lastQuestionAsked = question.date; break; } else { // If we couldn't interpret their input, ask them for it again. // Don't update the conversation flag, so that we repeat this step. await turnContext.sendActivity( result.message || "I'm sorry, I didn't understand that."); break; } // If we last asked for a date, record their response, confirm that we got it, // let them know the process is complete, and update the conversation flag. case question.date: result = this.validateDate(input); if (result.success) { profile.date = result.date; await turnContext.sendActivity(`Your cab ride to the airport is scheduled for ${profile.date}.`); await turnContext.sendActivity(`Thanks for completing the booking ${profile.name}.`); await turnContext.sendActivity('Type anything to run the bot again.'); flow.lastQuestionAsked = question.none; profile = {}; break; } else { // If we couldn't interpret their input, ask them for it again. // Don't update the conversation flag, so that we repeat this step. await turnContext.sendActivity( result.message || "I'm sorry, I didn't understand that."); break; } } } // Validates name input. Returns whether validation succeeded and either the parsed and normalized // value or a message the bot can use to ask the user again. static validateName(input) { const name = input && input.trim(); return name != undefined ? { success: true, name: name } : { success: false, message: 'Please enter a name that contains at least one character.' }; }; // Validates age input. Returns whether validation succeeded and either the parsed and normalized // value or a message the bot can use to ask the user again. static validateAge(input) { // Try to recognize the input as a number. This works for responses such as "twelve" as well as "12". try { // Attempt to convert the Recognizer result to an integer. This works for "a dozen", "twelve", "12", and so on. // The recognizer returns a list of potential recognition results, if any. const results = Recognizers.recognizeNumber(input, Recognizers.Culture.English); let output; results.forEach(function (result) { // result.resolution is a dictionary, where the "value" entry contains the processed string. const value = result.resolution['value']; if (value) { const age = parseInt(value); if (!isNaN(age) && age >= 18 && age <= 120) { output = { success: true, age: age }; return; } } }); return output || { success: false, message: 'Please enter an age between 18 and 120.' }; } catch (error) { return { success: false, message: "I'm sorry, I could not interpret that as an age. Please enter an age between 18 and 120." }; } } // Validates date input. Returns whether validation succeeded and either the parsed and normalized // value or a message the bot can use to ask the user again. static validateDate(input) { // Try to recognize the input as a date-time. This works for responses such as "11/14/2018", "today at 9pm", "tomorrow", "Sunday at 5pm", and so on. // The recognizer returns a list of potential recognition results, if any. try { const results = Recognizers.recognizeDateTime(input, Recognizers.Culture.English); const now = new Date(); const earliest = now.getTime() + (60 * 60 * 1000); let output; results.forEach(function (result) { // result.resolution is a dictionary, where the "values" entry contains the processed input. result.resolution['values'].forEach(function (resolution) { // The processed input contains a "value" entry if it is a date-time value, or "start" and // "end" entries if it is a date-time range. const datevalue = resolution['value'] || resolution['start']; // If only time is given, assume it's for today. const datetime = resolution['type'] === 'time' ? new Date(`${now.toLocaleDateString()} ${datevalue}`) : new Date(datevalue); if (datetime && earliest < datetime.getTime()) { output = { success: true, date: datetime.toLocaleDateString() }; return; } }); }); return output || { success: false, message: "I'm sorry, please enter a date at least an hour out." }; } catch (error) { return { success: false, message: "I'm sorry, I could not interpret that as an appropriate date. Please enter a date at least an hour out." }; } } } module.exports.MyBot = MyBot; ================================================ FILE: SDKV4-Samples/js/PromptUsersForInput/deploymentScripts/msbotClone/bot.recipe ================================================ { "version": "1.0", "resources": [ { "type": "endpoint", "id": "1", "name": "development", "url": "http://localhost:3978/api/messages" }, { "type": "endpoint", "id": "2", "name": "production", "url": "https://your-bot-url.azurewebsites.net/api/messages" }, { "type": "abs", "id": "3", "name": "simplePrompts-abs" }, { "type": "appInsights", "id": "4", "name": "simplePrompts-insights" }, { "type": "blob", "id": "5", "name": "simplePrompts-blob", "container": "botstatestore" } ] } ================================================ FILE: SDKV4-Samples/js/PromptUsersForInput/index.js ================================================ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. const path = require('path'); const restify = require('restify'); // Import required bot services. // See https://aka.ms/bot-services to learn more about the different parts of a bot. const { BotFrameworkAdapter, MemoryStorage, ConversationState, UserState } = require('botbuilder'); // Import required bot configuration. const { BotConfiguration } = require('botframework-config'); // This bot's main dialog. const { MyBot } = require('./bot'); // Read botFilePath and botFileSecret from .env file // Note: Ensure you have a .env file and include botFilePath and botFileSecret. const ENV_FILE = path.join(__dirname, '.env'); const env = require('dotenv').config({path: ENV_FILE}); // bot endpoint name as defined in .bot file // See https://aka.ms/about-bot-file to learn more about .bot file its use and bot configuration . const DEV_ENVIRONMENT = 'development'; // bot name as defined in .bot file // See https://aka.ms/about-bot-file to learn more about .bot file its use and bot configuration. const BOT_CONFIGURATION = (process.env.NODE_ENV || DEV_ENVIRONMENT); // Create HTTP server let server = restify.createServer(); server.listen(process.env.port || process.env.PORT || 3978, function () { console.log(`\n${server.name} listening to ${server.url}`); console.log(`\nGet Bot Framework Emulator: https://aka.ms/botframework-emulator`); console.log(`\nTo talk to your bot, open simplePrompts.bot file in the Emulator`); }); // .bot file path const BOT_FILE = path.join(__dirname, (process.env.botFilePath || '')); // Read bot configuration from .bot file. let botConfig; try { botConfig = BotConfiguration.loadSync(BOT_FILE, process.env.botFileSecret); } catch (err) { console.error(`\nError reading bot file. Please ensure you have valid botFilePath and botFileSecret set for your environment.`); console.error(`\n - The botFileSecret is available under appsettings for your Azure Bot Service bot.`); console.error(`\n - If you are running this bot locally, consider adding a .env file with botFilePath and botFileSecret.\n\n`); process.exit(); } // Get bot endpoint configuration by service name const endpointConfig = botConfig.findServiceByNameOrId(BOT_CONFIGURATION); // Create adapter. // See https://aka.ms/about-bot-adapter to learn more about .bot file its use and bot configuration . const adapter = new BotFrameworkAdapter({ appId: endpointConfig.appId || process.env.microsoftAppID, appPassword: endpointConfig.appPassword || process.env.microsoftAppPassword }); // Create conversation and user state with in-memory storage provider. const memoryStorage = new MemoryStorage(); const conversationState = new ConversationState(memoryStorage); const userState = new UserState(memoryStorage); // Create the bot. const myBot = new MyBot(conversationState, userState); // Catch-all for errors. adapter.onTurnError = async (context, error) => { // This check writes out errors to console log .vs. app insights. console.error(`\n [onTurnError]: ${error}`); // Send a message to the user context.sendActivity(`Oops. Something went wrong!`); // Clear out state await conversationState.load(context); await conversationState.clear(context); // Save state changes. await conversationState.saveChanges(context); }; // Listen for incoming requests. server.post('/api/messages', (req, res) => { adapter.processActivity(req, res, async (context) => { // Route to main dialog. await myBot.onTurn(context); }); }); ================================================ FILE: SDKV4-Samples/js/PromptUsersForInput/package.json ================================================ { "name": "simplePrompts", "version": "1.0.0", "description": "Sample code for Create your own prompts to gather user input", "author": "Microsoft Bot Builder Yeoman Generator v4.0.10", "license": "MIT", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build": "echo \"Error: no build specified\" && exit 1", "start": "node ./index.js", "watch": "nodemon ./index.js" }, "dependencies": { "@microsoft/recognizers-text-suite": "^1.1.4", "botbuilder": "^4.0.6", "botframework-config": "^4.0.6", "dotenv": "^6.0.0", "restify": "^6.3.4" }, "devDependencies": { "eslint": "^5.6.0", "eslint-config-standard": "^12.0.0", "eslint-plugin-import": "^2.14.0", "eslint-plugin-node": "^7.0.1", "eslint-plugin-promise": "^4.0.1", "eslint-plugin-standard": "^4.0.0", "nodemon": "^1.18.4", "@types/node": "10.10.2", "@types/restify": "7.2.4" } } ================================================ FILE: SDKV4-Samples/js/PromptUsersForInput/resources/echo.chat ================================================ user=Vishwac bot=EchoBot user: Hi bot: 1: You said "Hi" user: Hello bot: 2: You said "Hello" ================================================ FILE: SDKV4-Samples/js/PromptUsersForInput/simplePrompts.bot ================================================ { "name": "simplePrompts", "services": [ { "type": "endpoint", "name": "development", "endpoint": "http://localhost:3978/api/messages", "appId": "", "appPassword": "", "id": "1" } ], "padlock": "", "version": "2.0" } ================================================ FILE: SDKV4-Samples/js/complexDialogBot/.eslintrc.js ================================================ module.exports = { "extends": "standard", "rules": { "semi": [2, "always"], "indent": [2, 4], "no-return-await": 0, "space-before-function-paren": [2, { "named": "never", "anonymous": "never", "asyncArrow": "always" }], "template-curly-spacing": [2, "always"] } }; ================================================ FILE: SDKV4-Samples/js/complexDialogBot/.gitignore ================================================ node_modules .env ================================================ FILE: SDKV4-Samples/js/complexDialogBot/ComplexDialogBot.bot ================================================ { "name": "ComplexDialogBot", "description": "", "services": [ { "type": "abs", "tenantId": "72f988bf-86f1-41af-91ab-2d7cd011db47", "subscriptionId": "0389857f-2464-451b-ac83-5f54d565b1a7", "resourceGroup": "jf-registration-group", "serviceName": "ComplexDialogBot", "name": "ComplexDialogBot", "id": "1", "appId": "3dc6e586-761a-4f53-a86c-9068c13a8d34" }, { "type": "blob", "connectionString": "DefaultEndpointsProtocol=https;AccountName=omplexia3982;AccountKey=i5x0MbMJdInaLDAS/sMY0lg5jG/RGoGWlwJiguutN4pn0Ow2bb7JuX9+psWMKLOwfUW5MYnkGTWFxaQSCc4gsQ==;", "tenantId": "72f988bf-86f1-41af-91ab-2d7cd011db47", "subscriptionId": "0389857f-2464-451b-ac83-5f54d565b1a7", "resourceGroup": "jf-registration-group", "serviceName": "omplexia3982", "id": "2" }, { "type": "endpoint", "appId": "", "appPassword": "", "endpoint": "http://localhost:3978/api/messages", "name": "development", "id": "3" }, { "type": "endpoint", "appId": "3dc6e586-761a-4f53-a86c-9068c13a8d34", "appPassword": "nddakednI@h2b%!X", "endpoint": "https://omplexialogo59uk.azurewebsites.net/api/messages", "name": "production", "id": "4" }, { "type": "appInsights", "instrumentationKey": "b6f253df-2a26-4848-b4e7-f531057820fe", "applicationId": "5caae2b8-030b-4683-9713-f098a9a9e319", "apiKeys": {}, "tenantId": "72f988bf-86f1-41af-91ab-2d7cd011db47", "subscriptionId": "0389857f-2464-451b-ac83-5f54d565b1a7", "resourceGroup": "jf-registration-group", "serviceName": "ComplexDialogBotmbav3w", "id": "5" } ], "padlock": "", "version": "2.0" } ================================================ FILE: SDKV4-Samples/js/complexDialogBot/README.md ================================================ # ComplexDialogBot This sample creates a complex conversation with dialogs and Node.js. This bot has been created using [Microsoft Bot Framework][10], it shows how to create a simple echo bot with state. The bot maintains a simple counter that increases with each message from the user. This bot example uses [`restify`][1]. # To run the bot - Install modules and start the bot ```bash npm i & npm start ``` Alternatively you can also use nodemon via ```bash npm i & npm run watch ``` # Testing the bot using Bot Framework Emulator [Microsoft Bot Framework Emulator][2] is a desktop application that allows bot developers to test and debug their bots on localhost or running remotely through a tunnel. - Install the Bot Framework emulator from [here][3] ## Connect to bot using Bot Framework Emulator **V4** - Launch Bot Framework Emulator - File -> Open Bot Configuration - Select `ComplexDialogBot.bot` file # Further reading - [Azure Bot Service Introduction][6] - [Bot State][7] - [Write directly to storage][8] - [Managing conversation and user state][9] [1]: https://www.npmjs.com/package/restify [2]: https://github.com/microsoft/botframework-emulator [3]: https://aka.ms/botframework-emulator [6]: https://docs.microsoft.com/azure/bot-service/bot-service-overview-introduction [7]: https://docs.microsoft.com/azure/bot-service/bot-builder-storage-concept [8]: https://docs.microsoft.com/azure/bot-service/bot-builder-howto-v4-storage?tabs=javascript [9]: https://docs.microsoft.com/azure/bot-service/bot-builder-howto-v4-state?tabs=javascript [10] https://dev.botframework.com ================================================ FILE: SDKV4-Samples/js/complexDialogBot/bot.js ================================================ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. const { ActivityTypes } = require('botbuilder'); const { DialogSet, WaterfallDialog, TextPrompt, NumberPrompt, ChoicePrompt, DialogTurnStatus } = require('botbuilder-dialogs'); // Define state property accessor names. const DIALOG_STATE_PROPERTY = 'dialogStateProperty'; const USER_PROFILE_PROPERTY = 'userProfileProperty'; const WELCOME_TEXT = 'Welcome to ComplexDialogBot. This bot provides a complex conversation, with multiple dialogs. ' + 'Type anything to get started.'; // Define the dialog and prompt names for the bot. const TOP_LEVEL_DIALOG = 'dialog-topLevel'; const REVIEW_SELECTION_DIALOG = 'dialog-reviewSelection'; const NAME_PROMPT = 'prompt-name'; const AGE_PROMPT = 'prompt-age'; const SELECTION_PROMPT = 'prompt-companySelection'; // Define a 'done' response for the company selection prompt. const DONE_OPTION = 'done'; // Define value names for values tracked inside the dialogs. const USER_INFO = 'value-userInfo'; const COMPANIES_SELECTED = 'value-companiesSelected'; // Define the company choices for the company selection prompt. const COMPANY_OPTIONS = [ 'Adatum Corporation', 'Contoso Suites', 'Graphic Design Institute', 'Wide World Importers' ]; class MyBot { /** * * @param {ConversationState} conversation state object * @param {UserState} user state object */ constructor(conversationState, userState) { // Create the state property accessors and save the state management objects. this.dialogStateAccessor = conversationState.createProperty(DIALOG_STATE_PROPERTY); this.userProfileAccessor = userState.createProperty(USER_PROFILE_PROPERTY); this.conversationState = conversationState; this.userState = userState; // Create a dialog set for the bot. It requires a DialogState accessor, with which // to retrieve the dialog state from the turn context. this.dialogs = new DialogSet(this.dialogStateAccessor); // Add the prompts we need to the dialog set. this.dialogs .add(new TextPrompt(NAME_PROMPT)) .add(new NumberPrompt(AGE_PROMPT)) .add(new ChoicePrompt(SELECTION_PROMPT)); // Add the dialogs we need to the dialog set. this.dialogs.add(new WaterfallDialog(TOP_LEVEL_DIALOG) .addStep(this.nameStep.bind(this)) .addStep(this.ageStep.bind(this)) .addStep(this.startSelectionStep.bind(this)) .addStep(this.acknowledgementStep.bind(this))); this.dialogs.add(new WaterfallDialog(REVIEW_SELECTION_DIALOG) .addStep(this.selectionStep.bind(this)) .addStep(this.loopStep.bind(this))); } /** * * @param {TurnContext} on turn context object. */ async onTurn(turnContext) { if (turnContext.activity.type === ActivityTypes.Message) { // Run the DialogSet - let the framework identify the current state of the dialog from // the dialog stack and figure out what (if any) is the active dialog. const dialogContext = await this.dialogs.createContext(turnContext); const results = await dialogContext.continueDialog(); switch (results.status) { case DialogTurnStatus.cancelled: case DialogTurnStatus.empty: // If there is no active dialog, we should clear the user info and start a new dialog. await this.userProfileAccessor.set(turnContext, {}); await this.userState.saveChanges(turnContext); await dialogContext.beginDialog(TOP_LEVEL_DIALOG); break; case DialogTurnStatus.complete: // If we just finished the dialog, capture and display the results. const userInfo = results.result; const status = 'You are signed up to review ' + (userInfo.companiesToReview.length === 0 ? 'no companies' : userInfo.companiesToReview.join(' and ')) + '.'; await turnContext.sendActivity(status); await this.userProfileAccessor.set(turnContext, userInfo); await this.userState.saveChanges(turnContext); break; case DialogTurnStatus.waiting: // If there is an active dialog, we don't need to do anything here. break; } await this.conversationState.saveChanges(turnContext); } else if (turnContext.activity.type === ActivityTypes.ConversationUpdate) { if (turnContext.activity.membersAdded && turnContext.activity.membersAdded.length > 0) { await this.sendWelcomeMessage(turnContext); } } else { await turnContext.sendActivity(`[${turnContext.activity.type} event detected]`); } } // Sends a welcome message to any users who joined the conversation. async sendWelcomeMessage(turnContext) { for (var idx in turnContext.activity.membersAdded) { if (turnContext.activity.membersAdded[idx].id !== turnContext.activity.recipient.id) { await turnContext.sendActivity(WELCOME_TEXT); } } } async nameStep(stepContext) { // Create an object in which to collect the user's information within the dialog. stepContext.values[USER_INFO] = {}; // Ask the user to enter their name. return await stepContext.prompt(NAME_PROMPT, 'Please enter your name.'); } async ageStep(stepContext) { // Set the user's name to what they entered in response to the name prompt. stepContext.values[USER_INFO].name = stepContext.result; // Ask the user to enter their age. return await stepContext.prompt(AGE_PROMPT, 'Please enter your age.'); } async startSelectionStep(stepContext) { // Set the user's age to what they entered in response to the age prompt. stepContext.values[USER_INFO].age = stepContext.result; if (stepContext.result < 25) { // If they are too young, skip the review selection dialog, and pass an empty list to the next step. await stepContext.context.sendActivity('You must be 25 or older to participate.'); return await stepContext.next([]); } else { // Otherwise, start the review selection dialog. return await stepContext.beginDialog(REVIEW_SELECTION_DIALOG); } } async acknowledgementStep(stepContext) { // Set the user's company selection to what they entered in the review-selection dialog. const list = stepContext.result || []; stepContext.values[USER_INFO].companiesToReview = list; // Thank them for participating. await stepContext.context.sendActivity(`Thanks for participating, ${stepContext.values[USER_INFO].name}.`); // Exit the dialog, returning the collected user information. return await stepContext.endDialog(stepContext.values[USER_INFO]); } async selectionStep(stepContext) { // Continue using the same selection list, if any, from the previous iteration of this dialog. const list = Array.isArray(stepContext.options) ? stepContext.options : []; stepContext.values[COMPANIES_SELECTED] = list; // Create a prompt message. let message; if (list.length === 0) { message = 'Please choose a company to review, or `' + DONE_OPTION + '` to finish.'; } else { message = `You have selected **${list[0]}**. You can review an addition company, ` + 'or choose `' + DONE_OPTION + '` to finish.'; } // Create the list of options to choose from. const options = list.length > 0 ? COMPANY_OPTIONS.filter(function (item) { return item !== list[0] }) : COMPANY_OPTIONS.slice(); options.push(DONE_OPTION); // Prompt the user for a choice. return await stepContext.prompt(SELECTION_PROMPT, { prompt: message, retryPrompt: 'Please choose an option from the list.', choices: options }); } async loopStep(stepContext) { // Retrieve their selection list, the choice they made, and whether they chose to finish. const list = stepContext.values[COMPANIES_SELECTED]; const choice = stepContext.result; const done = choice.value === DONE_OPTION; if (!done) { // If they chose a company, add it to the list. list.push(choice.value); } if (done || list.length > 1) { // If they're done, exit and return their list. return await stepContext.endDialog(list); } else { // Otherwise, repeat this dialog, passing in the list from this iteration. return await stepContext.replaceDialog(REVIEW_SELECTION_DIALOG, list); } } } module.exports.MyBot = MyBot; ================================================ FILE: SDKV4-Samples/js/complexDialogBot/deploymentScripts/msbotClone/bot.recipe ================================================ { "version": "1.0", "resources": [ { "type": "endpoint", "id": "1", "name": "development", "url": "http://localhost:3978/api/messages" }, { "type": "endpoint", "id": "2", "name": "production", "url": "https://your-bot-url.azurewebsites.net/api/messages" }, { "type": "abs", "id": "3", "name": "ComplexDialogBot-abs" }, { "type": "appInsights", "id": "4", "name": "ComplexDialogBot-insights" }, { "type": "blob", "id": "5", "name": "ComplexDialogBot-blob", "container": "botstatestore" } ] } ================================================ FILE: SDKV4-Samples/js/complexDialogBot/iisnode.yml ================================================ nodeProcessCommandLine: "D:\Program Files (x86)\nodejs\8.9.4\node.exe" ================================================ FILE: SDKV4-Samples/js/complexDialogBot/index.js ================================================ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. const path = require('path'); const restify = require('restify'); // Import required bot services. // See https://aka.ms/bot-services to learn more about the different parts of a bot. const { BotFrameworkAdapter, MemoryStorage, UserState, ConversationState } = require('botbuilder'); // Import required bot configuration. const { BotConfiguration } = require('botframework-config'); // This bot's main dialog. const { MyBot } = require('./bot'); // Read botFilePath and botFileSecret from .env file // Note: Ensure you have a .env file and include botFilePath and botFileSecret. const ENV_FILE = path.join(__dirname, '.env'); const env = require('dotenv').config({path: ENV_FILE}); // bot endpoint name as defined in .bot file // See https://aka.ms/about-bot-file to learn more about .bot file its use and bot configuration . const DEV_ENVIRONMENT = 'development'; // bot name as defined in .bot file // See https://aka.ms/about-bot-file to learn more about .bot file its use and bot configuration. const BOT_CONFIGURATION = (process.env.NODE_ENV || DEV_ENVIRONMENT); // Create HTTP server let server = restify.createServer(); server.listen(process.env.port || process.env.PORT || 3978, function () { console.log(`\n${server.name} listening to ${server.url}`); console.log(`\nGet Bot Framework Emulator: https://aka.ms/botframework-emulator`); console.log(`\nTo talk to your bot, open ComplexDialogBot.bot file in the Emulator`); }); // .bot file path const BOT_FILE = path.join(__dirname, (process.env.botFilePath || '')); // Read bot configuration from .bot file. let botConfig; try { botConfig = BotConfiguration.loadSync(BOT_FILE, process.env.botFileSecret); } catch (err) { console.error(`\nError reading bot file. Please ensure you have valid botFilePath and botFileSecret set for your environment.`); console.error(`\n - The botFileSecret is available under appsettings for your Azure Bot Service bot.`); console.error(`\n - If you are running this bot locally, consider adding a .env file with botFilePath and botFileSecret.\n\n`); process.exit(); } // Get bot endpoint configuration by service name const endpointConfig = botConfig.findServiceByNameOrId(BOT_CONFIGURATION); // Create adapter. // See https://aka.ms/about-bot-adapter to learn more about .bot file its use and bot configuration . const adapter = new BotFrameworkAdapter({ appId: endpointConfig.appId || process.env.microsoftAppID, appPassword: endpointConfig.appPassword || process.env.microsoftAppPassword }); // Define state store for your bot. // See https://aka.ms/about-bot-state to learn more about bot state. const memoryStorage = new MemoryStorage(); // Create user and conversation state with in-memory storage provider. const userState = new UserState(memoryStorage); const conversationState = new ConversationState(memoryStorage); // Create the main dialog. const myBot = new MyBot(conversationState, userState); // Catch-all for errors. adapter.onTurnError = async (context, error) => { // This check writes out errors to console log .vs. app insights. console.error(`\n [onTurnError]: ${error}`); // Send a message to the user context.sendActivity(`Oops. Something went wrong!`); // Clear out state await conversationState.load(context); await conversationState.clear(context); // Save state changes. await conversationState.saveChanges(context); }; // Listen for incoming requests. server.post('/api/messages', (req, res) => { adapter.processActivity(req, res, async (context) => { // Route to main dialog. await myBot.onTurn(context); }); }); ================================================ FILE: SDKV4-Samples/js/complexDialogBot/package.json ================================================ { "name": "ComplexDialogBot", "version": "1.0.0", "description": "Sample code for the how to topic for creating advanced conversation flow", "author": "Microsoft Bot Builder Yeoman Generator v4.0.10", "license": "MIT", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build": "echo \"Error: no build specified\" && exit 1", "start": "node ./index.js", "watch": "nodemon ./index.js" }, "dependencies": { "botbuilder": "^4.0.6", "botbuilder-dialogs": "^4.2.0-preview.2251", "botframework-config": "^4.0.6", "dotenv": "^6.0.0", "restify": "^6.3.4" }, "devDependencies": { "eslint": "^5.6.0", "eslint-config-standard": "^12.0.0", "eslint-plugin-import": "^2.14.0", "eslint-plugin-node": "^7.0.1", "eslint-plugin-promise": "^4.0.1", "eslint-plugin-standard": "^4.0.0", "nodemon": "^1.18.4", "@types/node": "10.10.2", "@types/restify": "7.2.4" } } ================================================ FILE: SDKV4-Samples/js/complexDialogBot/web.config ================================================ ================================================ FILE: SDKV4-Samples/js/nlp-with-dispatch/QnAMaker.tsv ================================================ Question Answer Source Metadata hi Hello! QnAMaker.tsv greetings Hello! QnAMaker.tsv good morning Hello! QnAMaker.tsv good evening Hello! QnAMaker.tsv What are you? I am the LUIS-QnAMaker Dispatch bot! This sample demonstrates using several LUIS applications and QnA Maker knowledge base using dispatch. QnAMaker.tsv What? I am the LUIS-QnAMaker Dispatch bot! This sample demonstrates using several LUIS applications and QnA Maker knowledge base using dispatch. QnAMaker.tsv What do you do? I am the LUIS-QnAMaker Dispatch bot! This sample demonstrates using several LUIS applications and QnA Maker knowledge base using dispatch. QnAMaker.tsv Who are you? I am the LUIS-QnAMaker Dispatch bot! This sample demonstrates using several LUIS applications and QnA Maker knowledge base using dispatch. QnAMaker.tsv What is your name? I am the LUIS-QnAMaker Dispatch bot! This sample demonstrates using several LUIS applications and QnA Maker knowledge base using dispatch. QnAMaker.tsv What should I call you? I am the LUIS-QnAMaker Dispatch bot! This sample demonstrates using several LUIS applications and QnA Maker knowledge base using dispatch. QnAMaker.tsv ================================================ FILE: SDKV4-Samples/js/nlp-with-dispatch/home-automation.json ================================================ { "name": "Home Automation", "versionId": "0.1", "desc": "Home Automation LUIS application - Bot Builder Samples", "culture": "en-us", "intents": [ { "name": "HomeAutomation" }, { "name": "None" } ], "entities": [ { "name": "Device", "roles": [] }, { "name": "deviceProperty", "roles": [] }, { "name": "Room", "roles": [] } ], "closedLists": [ { "name": "Operation", "subLists": [ { "canonicalForm": "off", "list": [ "off", "turn off", "switch off", "lock", "out", "shut down", "stop" ] }, { "canonicalForm": "on", "list": [ "on", "turn on", "switch on", "unlock", "un lock", "boot up", "start" ] } ], "roles": [] } ], "composites": [], "patternAnyEntities": [ { "name": "Device_PatternAny", "explicitList": [], "roles": [] }, { "name": "Room_PatternAny", "explicitList": [], "roles": [] } ], "regex_entities": [], "prebuiltEntities": [ { "name": "number", "roles": [] } ], "regex_features": [], "model_features": [], "patterns": [ { "pattern": "turn off {Device_PatternAny} in {Room_PatternAny}", "intent": "HomeAutomation" }, { "pattern": "{Device_PatternAny} off [please]", "intent": "HomeAutomation" }, { "pattern": "turn on {Device_PatternAny}", "intent": "HomeAutomation" }, { "pattern": "{Device_PatternAny} on [please]", "intent": "HomeAutomation" }, { "pattern": "turn on {Device_PatternAny} in {Room_PatternAny}", "intent": "HomeAutomation" }, { "pattern": "{Device_PatternAny} in {Room_PatternAny} on [please]", "intent": "HomeAutomation" }, { "pattern": "{Device_PatternAny} in {Room_PatternAny} off [please]", "intent": "HomeAutomation" } ], "utterances": [ { "text": "breezeway on please", "intent": "HomeAutomation", "entities": [ { "startPos": 0, "endPos": 8, "entity": "Room" } ] }, { "text": "change temperature to seventy two degrees", "intent": "HomeAutomation", "entities": [ { "startPos": 7, "endPos": 17, "entity": "deviceProperty" } ] }, { "text": "coffee bar on please", "intent": "HomeAutomation", "entities": [ { "startPos": 0, "endPos": 9, "entity": "Device" } ] }, { "text": "decrease temperature for me please", "intent": "None", "entities": [ { "startPos": 9, "endPos": 19, "entity": "deviceProperty" } ] }, { "text": "dim kitchen lights to 25 .", "intent": "None", "entities": [ { "startPos": 4, "endPos": 10, "entity": "Room" }, { "startPos": 12, "endPos": 17, "entity": "Device" } ] }, { "text": "fish pond off please", "intent": "HomeAutomation", "entities": [ { "startPos": 0, "endPos": 8, "entity": "Device" } ] }, { "text": "fish pond on please", "intent": "HomeAutomation", "entities": [ { "startPos": 0, "endPos": 8, "entity": "Device" } ] }, { "text": "illuminate please", "intent": "HomeAutomation", "entities": [ { "startPos": 0, "endPos": 9, "entity": "Device" } ] }, { "text": "living room lamp on please", "intent": "HomeAutomation", "entities": [ { "startPos": 0, "endPos": 10, "entity": "Room" }, { "startPos": 12, "endPos": 15, "entity": "Device" } ] }, { "text": "living room lamps off please", "intent": "HomeAutomation", "entities": [ { "startPos": 0, "endPos": 10, "entity": "Room" }, { "startPos": 12, "endPos": 16, "entity": "Device" } ] }, { "text": "lock the doors for me please", "intent": "HomeAutomation", "entities": [ { "startPos": 9, "endPos": 13, "entity": "Device" } ] }, { "text": "lower your volume", "intent": "None", "entities": [ { "startPos": 11, "endPos": 16, "entity": "deviceProperty" } ] }, { "text": "make camera 1 off please", "intent": "HomeAutomation", "entities": [ { "startPos": 5, "endPos": 12, "entity": "Device" } ] }, { "text": "make some coffee", "intent": "HomeAutomation", "entities": [ { "startPos": 10, "endPos": 15, "entity": "Device" } ] }, { "text": "play dvd", "intent": "HomeAutomation", "entities": [ { "startPos": 5, "endPos": 7, "entity": "Device" } ] }, { "text": "set lights out in bedroom", "intent": "HomeAutomation", "entities": [ { "startPos": 4, "endPos": 9, "entity": "Device" }, { "startPos": 18, "endPos": 24, "entity": "Room" } ] }, { "text": "set lights to bright", "intent": "HomeAutomation", "entities": [ { "startPos": 4, "endPos": 9, "entity": "Device" }, { "startPos": 14, "endPos": 19, "entity": "deviceProperty" } ] }, { "text": "set lights to concentrate", "intent": "HomeAutomation", "entities": [ { "startPos": 4, "endPos": 9, "entity": "Device" }, { "startPos": 14, "endPos": 24, "entity": "deviceProperty" } ] }, { "text": "shut down my work computer", "intent": "HomeAutomation", "entities": [ { "startPos": 18, "endPos": 25, "entity": "Device" } ] }, { "text": "snap switch fan fifty percent", "intent": "HomeAutomation", "entities": [ { "startPos": 12, "endPos": 14, "entity": "Device" } ] }, { "text": "start master bedroom light.", "intent": "HomeAutomation", "entities": [ { "startPos": 6, "endPos": 19, "entity": "Room" }, { "startPos": 21, "endPos": 25, "entity": "Device" } ] }, { "text": "theater on please", "intent": "HomeAutomation", "entities": [ { "startPos": 0, "endPos": 6, "entity": "Room" } ] }, { "text": "turn dimmer off", "intent": "HomeAutomation", "entities": [ { "startPos": 5, "endPos": 10, "entity": "Device" } ] }, { "text": "turn off ac please", "intent": "HomeAutomation", "entities": [ { "startPos": 9, "endPos": 10, "entity": "Device" } ] }, { "text": "turn off foyer lights", "intent": "HomeAutomation", "entities": [ { "startPos": 9, "endPos": 13, "entity": "Room" }, { "startPos": 15, "endPos": 20, "entity": "Device" } ] }, { "text": "turn off living room light", "intent": "HomeAutomation", "entities": [ { "startPos": 9, "endPos": 19, "entity": "Room" }, { "startPos": 21, "endPos": 25, "entity": "Device" } ] }, { "text": "turn off staircase", "intent": "HomeAutomation", "entities": [ { "startPos": 9, "endPos": 17, "entity": "Device" } ] }, { "text": "turn off venice lamp", "intent": "HomeAutomation", "entities": [ { "startPos": 16, "endPos": 19, "entity": "Device" } ] }, { "text": "turn on bathroom heater", "intent": "HomeAutomation", "entities": [ { "startPos": 8, "endPos": 15, "entity": "Room" }, { "startPos": 17, "endPos": 22, "entity": "Device" } ] }, { "text": "turn on external speaker", "intent": "HomeAutomation", "entities": [ { "startPos": 8, "endPos": 23, "entity": "Device" } ] }, { "text": "turn on kitchen faucet", "intent": "HomeAutomation", "entities": [ { "startPos": 8, "endPos": 14, "entity": "Room" }, { "startPos": 16, "endPos": 21, "entity": "Device" } ] }, { "text": "turn on light in bedroom", "intent": "HomeAutomation", "entities": [ { "startPos": 8, "endPos": 12, "entity": "Device" }, { "startPos": 17, "endPos": 23, "entity": "Room" } ] }, { "text": "turn on my bedroom lights.", "intent": "HomeAutomation", "entities": [ { "startPos": 11, "endPos": 17, "entity": "Room" }, { "startPos": 19, "endPos": 24, "entity": "Device" } ] }, { "text": "turn on the furnace room lights", "intent": "HomeAutomation", "entities": [ { "startPos": 12, "endPos": 23, "entity": "Room" }, { "startPos": 25, "endPos": 30, "entity": "Device" } ] }, { "text": "turn on the internet in my bedroom please", "intent": "None", "entities": [ { "startPos": 27, "endPos": 33, "entity": "Room" } ] }, { "text": "turn on thermostat please", "intent": "HomeAutomation", "entities": [ { "startPos": 8, "endPos": 17, "entity": "Device" } ] }, { "text": "turn the fan to high", "intent": "HomeAutomation", "entities": [ { "startPos": 9, "endPos": 11, "entity": "Device" } ] }, { "text": "turn thermostat on 70.", "intent": "HomeAutomation", "entities": [ { "startPos": 5, "endPos": 14, "entity": "Device" } ] } ] } ================================================ FILE: SDKV4-Samples/js/nlp-with-dispatch/nlp-with-dispatchDispatch.json ================================================ { "name": "nlp-with-dispatchDispatch", "versionId": "Dispatch", "desc": "Dispatch model for nlp-with-dispatchDispatch", "culture": "en-us", "intents": [ { "name": "l_Home Automation" }, { "name": "l_Weather" }, { "name": "None" }, { "name": "q_sample-qna" } ], "entities": [], "closedLists": [], "composites": [], "patternAnyEntities": [], "regex_entities": [], "prebuiltEntities": [], "regex_features": [], "model_features": [], "patterns": [], "utterances": [ { "text": "breezeway on please", "intent": "l_Home Automation", "entities": [] }, { "text": "change temperature to seventy two degrees", "intent": "l_Home Automation", "entities": [] }, { "text": "coffee bar on please", "intent": "l_Home Automation", "entities": [] }, { "text": "current weather ?", "intent": "l_Weather", "entities": [] }, { "text": "decrease temperature for me please", "intent": "None", "entities": [] }, { "text": "dim kitchen lights to 25 .", "intent": "None", "entities": [] }, { "text": "do florida residents usually need ice scrapers", "intent": "l_Weather", "entities": [] }, { "text": "fish pond off please", "intent": "l_Home Automation", "entities": [] }, { "text": "fish pond on please", "intent": "l_Home Automation", "entities": [] }, { "text": "forecast in celcius", "intent": "l_Weather", "entities": [] }, { "text": "get florence temperature in september", "intent": "l_Weather", "entities": [] }, { "text": "get for me the weather conditions in sonoma county", "intent": "l_Weather", "entities": [] }, { "text": "get the daily temperature greenwood indiana", "intent": "l_Weather", "entities": [] }, { "text": "get the forcast for me", "intent": "l_Weather", "entities": [] }, { "text": "get the weather at saint george utah", "intent": "l_Weather", "entities": [] }, { "text": "good evening", "intent": "q_sample-qna", "entities": [] }, { "text": "good morning", "intent": "q_sample-qna", "entities": [] }, { "text": "greetings", "intent": "q_sample-qna", "entities": [] }, { "text": "hi", "intent": "q_sample-qna", "entities": [] }, { "text": "how much rain does chambersburg get a year", "intent": "l_Weather", "entities": [] }, { "text": "i want to know the temperature at death valley", "intent": "l_Weather", "entities": [] }, { "text": "illuminate please", "intent": "l_Home Automation", "entities": [] }, { "text": "living room lamp on please", "intent": "l_Home Automation", "entities": [] }, { "text": "living room lamps off please", "intent": "l_Home Automation", "entities": [] }, { "text": "lock the doors for me please", "intent": "l_Home Automation", "entities": [] }, { "text": "lower your volume", "intent": "None", "entities": [] }, { "text": "make camera 1 off please", "intent": "l_Home Automation", "entities": [] }, { "text": "make some coffee", "intent": "l_Home Automation", "entities": [] }, { "text": "play dvd", "intent": "l_Home Automation", "entities": [] }, { "text": "provide me by toronto weather please", "intent": "l_Weather", "entities": [] }, { "text": "set lights out in bedroom", "intent": "l_Home Automation", "entities": [] }, { "text": "set lights to bright", "intent": "l_Home Automation", "entities": [] }, { "text": "set lights to concentrate", "intent": "l_Home Automation", "entities": [] }, { "text": "show average rainfall for boise", "intent": "l_Weather", "entities": [] }, { "text": "show me the forecast at alabama", "intent": "l_Weather", "entities": [] }, { "text": "shut down my work computer", "intent": "l_Home Automation", "entities": [] }, { "text": "snap switch fan fifty percent", "intent": "l_Home Automation", "entities": [] }, { "text": "soliciting today's weather", "intent": "l_Weather", "entities": [] }, { "text": "start master bedroom light.", "intent": "l_Home Automation", "entities": [] }, { "text": "temperature of delhi in celsius please", "intent": "l_Weather", "entities": [] }, { "text": "theater on please", "intent": "l_Home Automation", "entities": [] }, { "text": "turn dimmer off", "intent": "l_Home Automation", "entities": [] }, { "text": "turn off ac please", "intent": "l_Home Automation", "entities": [] }, { "text": "turn off foyer lights", "intent": "l_Home Automation", "entities": [] }, { "text": "turn off living room light", "intent": "l_Home Automation", "entities": [] }, { "text": "turn off staircase", "intent": "l_Home Automation", "entities": [] }, { "text": "turn off venice lamp", "intent": "l_Home Automation", "entities": [] }, { "text": "turn on bathroom heater", "intent": "l_Home Automation", "entities": [] }, { "text": "turn on external speaker", "intent": "l_Home Automation", "entities": [] }, { "text": "turn on kitchen faucet", "intent": "l_Home Automation", "entities": [] }, { "text": "turn on light in bedroom", "intent": "l_Home Automation", "entities": [] }, { "text": "turn on my bedroom lights.", "intent": "l_Home Automation", "entities": [] }, { "text": "turn on the furnace room lights", "intent": "l_Home Automation", "entities": [] }, { "text": "turn on the internet in my bedroom please", "intent": "None", "entities": [] }, { "text": "turn on thermostat please", "intent": "l_Home Automation", "entities": [] }, { "text": "turn the fan to high", "intent": "l_Home Automation", "entities": [] }, { "text": "turn thermostat on 70.", "intent": "l_Home Automation", "entities": [] }, { "text": "was last year about this time as wet as it is now in the south ?", "intent": "l_Weather", "entities": [] }, { "text": "what are you?", "intent": "q_sample-qna", "entities": [] }, { "text": "what do you do?", "intent": "q_sample-qna", "entities": [] }, { "text": "what is the rain volume in sonoma county ?", "intent": "l_Weather", "entities": [] }, { "text": "what is the weather in redmond ?", "intent": "l_Weather", "entities": [] }, { "text": "what is the weather today at 10 day durham ?", "intent": "l_Weather", "entities": [] }, { "text": "what is your name?", "intent": "q_sample-qna", "entities": [] }, { "text": "what should i call you?", "intent": "q_sample-qna", "entities": [] }, { "text": "what to wear in march in california", "intent": "l_Weather", "entities": [] }, { "text": "what will the weather be tomorrow in new york ?", "intent": "l_Weather", "entities": [] }, { "text": "what?", "intent": "q_sample-qna", "entities": [] }, { "text": "what's the weather going to be like in hawaii ?", "intent": "l_Weather", "entities": [] }, { "text": "what's the weather like in minneapolis", "intent": "l_Weather", "entities": [] }, { "text": "who are you?", "intent": "q_sample-qna", "entities": [] }, { "text": "will it be raining in ranchi", "intent": "l_Weather", "entities": [] }, { "text": "will it rain this weekend", "intent": "l_Weather", "entities": [] }, { "text": "will it snow today", "intent": "l_Weather", "entities": [] } ] } ================================================ FILE: SDKV4-Samples/js/nlp-with-dispatch/weather.json ================================================ { "name": "Weather", "versionId": "0.1", "desc": "Weather LUIS application - Bot Builder Samples", "culture": "en-us", "intents": [ { "name": "Get Weather Condition" }, { "name": "Get Weather Forecast" }, { "name": "None" } ], "entities": [ { "name": "Location", "roles": [] } ], "closedLists": [], "composites": [], "patternAnyEntities": [ { "name": "Location_PatternAny", "explicitList": [], "roles": [] } ], "regex_entities": [], "prebuiltEntities": [], "regex_features": [], "model_features": [], "patterns": [ { "pattern": "weather in {Location_PatternAny}", "intent": "Get Weather Condition" }, { "pattern": "how's the weather in {Location_PatternAny}", "intent": "Get Weather Condition" }, { "pattern": "current weather in {Location_PatternAny}", "intent": "Get Weather Condition" }, { "pattern": "what's the forecast for next week in {Location_PatternAny}", "intent": "Get Weather Forecast" }, { "pattern": "show me the forecast for {Location_PatternAny}", "intent": "Get Weather Forecast" }, { "pattern": "what's the forecast for {Location_PatternAny}", "intent": "Get Weather Forecast" } ], "utterances": [ { "text": "current weather ?", "intent": "Get Weather Condition", "entities": [] }, { "text": "do florida residents usually need ice scrapers", "intent": "Get Weather Condition", "entities": [ { "startPos": 3, "endPos": 9, "entity": "Location" } ] }, { "text": "forecast in celcius", "intent": "Get Weather Forecast", "entities": [] }, { "text": "get florence temperature in september", "intent": "Get Weather Condition", "entities": [ { "startPos": 4, "endPos": 11, "entity": "Location" } ] }, { "text": "get for me the weather conditions in sonoma county", "intent": "Get Weather Condition", "entities": [ { "startPos": 37, "endPos": 49, "entity": "Location" } ] }, { "text": "get the daily temperature greenwood indiana", "intent": "Get Weather Condition", "entities": [ { "startPos": 26, "endPos": 42, "entity": "Location" } ] }, { "text": "get the forcast for me", "intent": "Get Weather Forecast", "entities": [] }, { "text": "get the weather at saint george utah", "intent": "Get Weather Condition", "entities": [ { "startPos": 19, "endPos": 35, "entity": "Location" } ] }, { "text": "how much rain does chambersburg get a year", "intent": "Get Weather Condition", "entities": [ { "startPos": 19, "endPos": 30, "entity": "Location" } ] }, { "text": "i want to know the temperature at death valley", "intent": "Get Weather Forecast", "entities": [ { "startPos": 34, "endPos": 45, "entity": "Location" } ] }, { "text": "provide me by toronto weather please", "intent": "Get Weather Forecast", "entities": [ { "startPos": 14, "endPos": 20, "entity": "Location" } ] }, { "text": "show average rainfall for boise", "intent": "Get Weather Condition", "entities": [ { "startPos": 26, "endPos": 30, "entity": "Location" } ] }, { "text": "show me the forecast at alabama", "intent": "Get Weather Forecast", "entities": [ { "startPos": 24, "endPos": 30, "entity": "Location" } ] }, { "text": "soliciting today's weather", "intent": "Get Weather Forecast", "entities": [] }, { "text": "temperature of delhi in celsius please", "intent": "Get Weather Condition", "entities": [ { "startPos": 15, "endPos": 19, "entity": "Location" } ] }, { "text": "was last year about this time as wet as it is now in the south ?", "intent": "Get Weather Condition", "entities": [ { "startPos": 57, "endPos": 61, "entity": "Location" } ] }, { "text": "what is the rain volume in sonoma county ?", "intent": "Get Weather Condition", "entities": [ { "startPos": 27, "endPos": 39, "entity": "Location" } ] }, { "text": "what is the weather in redmond ?", "intent": "Get Weather Forecast", "entities": [ { "startPos": 23, "endPos": 29, "entity": "Location" } ] }, { "text": "what is the weather today at 10 day durham ?", "intent": "Get Weather Forecast", "entities": [ { "startPos": 36, "endPos": 41, "entity": "Location" } ] }, { "text": "what to wear in march in california", "intent": "Get Weather Condition", "entities": [ { "startPos": 25, "endPos": 34, "entity": "Location" } ] }, { "text": "what will the weather be tomorrow in new york ?", "intent": "Get Weather Forecast", "entities": [ { "startPos": 37, "endPos": 44, "entity": "Location" } ] }, { "text": "what's the weather going to be like in hawaii ?", "intent": "Get Weather Forecast", "entities": [ { "startPos": 39, "endPos": 44, "entity": "Location" } ] }, { "text": "what's the weather like in minneapolis", "intent": "Get Weather Condition", "entities": [ { "startPos": 27, "endPos": 37, "entity": "Location" } ] }, { "text": "will it be raining in ranchi", "intent": "Get Weather Forecast", "entities": [ { "startPos": 22, "endPos": 27, "entity": "Location" } ] }, { "text": "will it rain this weekend", "intent": "Get Weather Forecast", "entities": [] }, { "text": "will it snow today", "intent": "Get Weather Forecast", "entities": [] } ] } ================================================ FILE: SDKV4-Samples/js/nlp-with-luis/reminders-with-entities.json ================================================ { "intents": [ { "name": "Calendar_Add" }, { "name": "Calendar_Find" }, { "name": "None" } ], "entities": [ { "name": "Appointment", "roles": [] }, { "name": "Meeting", "roles": [] }, { "name": "Schedule", "roles": [] } ], "composites": [], "closedLists": [], "patternAnyEntities": [], "regex_entities": [], "prebuiltEntities": [], "model_features": [], "regex_features": [], "patterns": [], "utterances": [ { "text": "add a new event on 27 - apr", "intent": "Calendar_Add", "entities": [] }, { "text": "add a new task finish assignment", "intent": "Calendar_Add", "entities": [] }, { "text": "add an event to read about adam lambert news", "intent": "Calendar_Add", "entities": [] }, { "text": "add an event to visit 209 nashville gym", "intent": "Calendar_Add", "entities": [] }, { "text": "add date to my schedule", "intent": "Calendar_Add", "entities": [ { "entity": "Schedule", "startPos": 15, "endPos": 22 } ] }, { "text": "add imax theater to my upcoming events", "intent": "Calendar_Add", "entities": [] }, { "text": "am i free to be with friends saturday ?", "intent": "None", "entities": [] }, { "text": "appointment with johnson needs to be next week", "intent": "None", "entities": [ { "entity": "Appointment", "startPos": 0, "endPos": 10 } ] }, { "text": "calendar for november 1948", "intent": "Calendar_Find", "entities": [] }, { "text": "calendar i ' ll be at the garage from 8 till 3 this saturday", "intent": "Calendar_Add", "entities": [] }, { "text": "call dad mike", "intent": "None", "entities": [] }, { "text": "change the meeting with chris to 9 : 00 am", "intent": "None", "entities": [] }, { "text": "display weekend plans", "intent": "Calendar_Find", "entities": [] }, { "text": "do i have anything on wednesday ?", "intent": "Calendar_Find", "entities": [] }, { "text": "dunmore pa sonic sounds friday morning please", "intent": "Calendar_Add", "entities": [] }, { "text": "email cloney john", "intent": "None", "entities": [] }, { "text": "extend lunch meeting 30 minutes extra", "intent": "None", "entities": [ { "entity": "Meeting", "startPos": 13, "endPos": 19 } ] }, { "text": "how many days are there between march 13th 2015 and today ?", "intent": "Calendar_Find", "entities": [] }, { "text": "i want to reschedule the meeting at the air force club", "intent": "None", "entities": [] }, { "text": "marketing meetings on tuesdays will now be every wednesday please change on my calendar", "intent": "None", "entities": [] }, { "text": "meet my manager", "intent": "Calendar_Add", "entities": [] }, { "text": "meeting my manager", "intent": "Calendar_Add", "entities": [] }, { "text": "move the bbq party to friday", "intent": "None", "entities": [] }, { "text": "pull up my appointment find out how much time i have before my next appointment", "intent": "Calendar_Find", "entities": [ { "entity": "Appointment", "startPos": 11, "endPos": 21 } ] }, { "text": "save the date may 17 pictures party", "intent": "Calendar_Add", "entities": [] }, { "text": "schedule a conference call for tomorrow", "intent": "Calendar_Add", "entities": [ { "entity": "Schedule", "startPos": 0, "endPos": 7 } ] }, { "text": "schedule a meeting for tomorrow", "intent": "Calendar_Add", "entities": [ { "entity": "Meeting", "startPos": 11, "endPos": 17 } ] }, { "text": "schedule appointment for tomorrow please", "intent": "Calendar_Add", "entities": [ { "entity": "Schedule", "startPos": 0, "endPos": 7 }, { "entity": "Appointment", "startPos": 9, "endPos": 19 } ] }, { "text": "search for meetings with chris", "intent": "Calendar_Find", "entities": [ { "entity": "Meeting", "startPos": 11, "endPos": 18 } ] }, { "text": "show me tomorrow ' s wedding party time", "intent": "Calendar_Find", "entities": [] }, { "text": "show my schedule for tomorrow", "intent": "Calendar_Find", "entities": [ { "entity": "Schedule", "startPos": 8, "endPos": 15 } ] }, { "text": "tell me the event details", "intent": "Calendar_Find", "entities": [] }, { "text": "the meeting will last for one hour", "intent": "Calendar_Add", "entities": [ { "entity": "Meeting", "startPos": 4, "endPos": 10 } ] }, { "text": "the workshop will last for 10 hours", "intent": "None", "entities": [] }, { "text": "voice activated reading of appointments this week", "intent": "Calendar_Find", "entities": [ { "entity": "Appointment", "startPos": 27, "endPos": 38 } ] } ], "settings": [] } ================================================ FILE: SDKV4-Samples/js/stateBot/.eslintrc.js ================================================ module.exports = { "extends": "standard", "rules": { "semi": [2, "always"], "indent": [2, 4], "no-return-await": 0, "space-before-function-paren": [2, { "named": "never", "anonymous": "never", "asyncArrow": "always" }], "template-curly-spacing": [2, "always"] } }; ================================================ FILE: SDKV4-Samples/js/stateBot/.gitignore ================================================ node_modules .env ================================================ FILE: SDKV4-Samples/js/stateBot/README.md ================================================ # Save user and conversation data This sample demonstrates how to save user and conversation data in a Node.js bot. The bot maintains conversation state to track and direct the conversation and ask the user questions. The bot maintains user state to track the user's answers. # To run the bot - Install modules and start the bot ```bash npm i & npm start ``` Alternatively you can also use nodemon via ```bash npm i & npm run watch ``` # Testing the bot using Bot Framework Emulator [Microsoft Bot Framework Emulator][2] is a desktop application that allows bot developers to test and debug their bots on localhost or running remotely through a tunnel. - Install the Bot Framework emulator from [here][3] ## Connect to bot using Bot Framework Emulator **V4** - Launch Bot Framework Emulator - File -> Open Bot Configuration - Select `stateBot.bot` file # Further reading - [Azure Bot Service Introduction][6] - [Bot State][7] - [Write directly to storage][8] - [Managing conversation and user state][9] [1]: https://www.npmjs.com/package/restify [2]: https://github.com/microsoft/botframework-emulator [3]: https://aka.ms/botframework-emulator [4]: https://docs.microsoft.com/azure/bot-service/bot-builder-howto-v4-state?tabs=js [5]: https://github.com/microsoft/botbuilder-tools [6]: https://docs.microsoft.com/azure/bot-service/bot-service-overview-introduction [7]: https://docs.microsoft.com/azure/bot-service/bot-builder-storage-concept [8]: https://docs.microsoft.com/azure/bot-service/bot-builder-howto-v4-storage?tabs=js [9]: https://docs.microsoft.com/azure/bot-service/bot-builder-howto-v4-state?tabs=js [10] https://dev.botframework.com ================================================ FILE: SDKV4-Samples/js/stateBot/bot.js ================================================ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. const { ActivityTypes } = require('botbuilder'); // The accessor names for the conversation data and user profile state property accessors. const CONVERSATION_DATA_PROPERTY = 'conversationData'; const USER_PROFILE_PROPERTY = 'userProfile'; class MyBot { /** * * @param {ConversationState} conversation state object */ constructor(conversationState, userState) { // Create the state property accessors for the conversation data and user profile. this.conversationData = conversationState.createProperty(CONVERSATION_DATA_PROPERTY); this.userProfile = userState.createProperty(USER_PROFILE_PROPERTY); // The state management objects for the conversation and user state. this.conversationState = conversationState; this.userState = userState; } /** * * @param {TurnContext} on turn context object. */ async onTurn(turnContext) { // See https://aka.ms/about-bot-activity-message to learn more about the message and other activity types. if (turnContext.activity.type === ActivityTypes.Message) { // Get the state properties from the turn context. const userProfile = await this.userProfile.get(turnContext, {}); const conversationData = await this.conversationData.get( turnContext, { promptedForUserName: false }); if (!userProfile.name) { // First time around this is undefined, so we will prompt user for name. if (conversationData.promptedForUserName) { // Set the name to what the user provided. userProfile.name = turnContext.activity.text; // Acknowledge that we got their name. await turnContext.sendActivity(`Thanks ${userProfile.name}.`); // Reset the flag to allow the bot to go though the cycle again. conversationData.promptedForUserName = false; } else { // Prompt the user for their name. await turnContext.sendActivity('What is your name?'); // Set the flag to true, so we don't prompt in the next turn. conversationData.promptedForUserName = true; } // Save user state and save changes. await this.userProfile.set(turnContext, userProfile); await this.userState.saveChanges(turnContext); } else { // Add message details to the conversation data. conversationData.timestamp = turnContext.activity.timestamp.toLocaleString(); conversationData.channelId = turnContext.activity.channelId; // Display state data. await turnContext.sendActivity(`${userProfile.name} sent: ${turnContext.activity.text}`); await turnContext.sendActivity(`Message received at: ${conversationData.timestamp}`); await turnContext.sendActivity(`Message received from: ${conversationData.channelId}`); } // Update conversation state and save changes. await this.conversationData.set(turnContext, conversationData); await this.conversationState.saveChanges(turnContext); } } } module.exports.MyBot = MyBot; ================================================ FILE: SDKV4-Samples/js/stateBot/deploymentScripts/msbotClone/bot.recipe ================================================ { "version": "1.0", "resources": [ { "type": "endpoint", "id": "1", "name": "development", "url": "http://localhost:3978/api/messages" }, { "type": "endpoint", "id": "2", "name": "production", "url": "https://your-bot-url.azurewebsites.net/api/messages" }, { "type": "abs", "id": "3", "name": "stateBot-abs" }, { "type": "appInsights", "id": "4", "name": "stateBot-insights" }, { "type": "blob", "id": "5", "name": "stateBot-blob", "container": "botstatestore" } ] } ================================================ FILE: SDKV4-Samples/js/stateBot/index.js ================================================ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. const path = require('path'); const restify = require('restify'); // Import required bot services. // See https://aka.ms/bot-services to learn more about the different parts of a bot. const { BotFrameworkAdapter, MemoryStorage, ConversationState, UserState } = require('botbuilder'); // Import required bot configuration. const { BotConfiguration } = require('botframework-config'); // This bot's main dialog. const { MyBot } = require('./bot'); // Read botFilePath and botFileSecret from .env file // Note: Ensure you have a .env file and include botFilePath and botFileSecret. const ENV_FILE = path.join(__dirname, '.env'); const env = require('dotenv').config({path: ENV_FILE}); // bot endpoint name as defined in .bot file // See https://aka.ms/about-bot-file to learn more about .bot file its use and bot configuration . const DEV_ENVIRONMENT = 'development'; // bot name as defined in .bot file // See https://aka.ms/about-bot-file to learn more about .bot file its use and bot configuration. const BOT_CONFIGURATION = (process.env.NODE_ENV || DEV_ENVIRONMENT); // Create HTTP server let server = restify.createServer(); server.listen(process.env.port || process.env.PORT || 3978, function () { console.log(`\n${server.name} listening to ${server.url}`); console.log(`\nGet Bot Framework Emulator: https://aka.ms/botframework-emulator`); console.log(`\nTo talk to your bot, open stateBot.bot file in the Emulator`); }); // .bot file path const BOT_FILE = path.join(__dirname, (process.env.botFilePath || '')); // Read bot configuration from .bot file. let botConfig; try { botConfig = BotConfiguration.loadSync(BOT_FILE, process.env.botFileSecret); } catch (err) { console.error(`\nError reading bot file. Please ensure you have valid botFilePath and botFileSecret set for your environment.`); console.error(`\n - The botFileSecret is available under appsettings for your Azure Bot Service bot.`); console.error(`\n - If you are running this bot locally, consider adding a .env file with botFilePath and botFileSecret.\n\n`); process.exit(); } // Get bot endpoint configuration by service name const endpointConfig = botConfig.findServiceByNameOrId(BOT_CONFIGURATION); // Create adapter. // See https://aka.ms/about-bot-adapter to learn more about .bot file its use and bot configuration . const adapter = new BotFrameworkAdapter({ appId: endpointConfig.appId || process.env.microsoftAppID, appPassword: endpointConfig.appPassword || process.env.microsoftAppPassword }); // Define state store for your bot. // See https://aka.ms/about-bot-state to learn more about bot state. const memoryStorage = new MemoryStorage(); // Create conversation and user state with in-memory storage provider. const conversationState = new ConversationState(memoryStorage); const userState = new UserState(memoryStorage); // Create the bot. const myBot = new MyBot(conversationState, userState); // Catch-all for errors. adapter.onTurnError = async (context, error) => { // This check writes out errors to console log .vs. app insights. console.error(`\n [onTurnError]: ${error}`); // Send a message to the user context.sendActivity(`Oops. Something went wrong!`); // Clear out state await conversationState.load(context); await conversationState.clear(context); // Save state changes. await conversationState.saveChanges(context); }; // Listen for incoming requests. server.post('/api/messages', (req, res) => { adapter.processActivity(req, res, async (context) => { // Route to main dialog. await myBot.onTurn(context); }); }); ================================================ FILE: SDKV4-Samples/js/stateBot/package.json ================================================ { "name": "stateBot", "version": "1.0.0", "description": "Demonstrates how to set up user and conversation state.", "author": "Microsoft Bot Builder Yeoman Generator v4.0.10", "license": "MIT", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build": "echo \"Error: no build specified\" && exit 1", "start": "node ./index.js", "watch": "nodemon ./index.js" }, "dependencies": { "botbuilder": "^4.0.6", "botframework-config": "^4.0.6", "dotenv": "^6.0.0", "restify": "^6.3.4" }, "devDependencies": { "eslint": "^5.6.0", "eslint-config-standard": "^12.0.0", "eslint-plugin-import": "^2.14.0", "eslint-plugin-node": "^7.0.1", "eslint-plugin-promise": "^4.0.1", "eslint-plugin-standard": "^4.0.0", "nodemon": "^1.18.4", "@types/node": "10.10.2", "@types/restify": "7.2.4" } } ================================================ FILE: SDKV4-Samples/js/stateBot/resources/echo.chat ================================================ user=Vishwac bot=EchoBot user: Hi bot: 1: You said "Hi" user: Hello bot: 2: You said "Hello" ================================================ FILE: SDKV4-Samples/js/stateBot/stateBot.bot ================================================ { "name": "stateBot", "services": [ { "type": "endpoint", "name": "development", "endpoint": "http://localhost:3978/api/messages", "appId": "", "appPassword": "", "id": "1" } ], "padlock": "", "version": "2.0" } ================================================ FILE: SECURITY.md ================================================ ## Security Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below. ## Reporting Security Issues **Please do not report security vulnerabilities through public GitHub issues.** Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report). If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey). You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc). Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) * Full paths of source file(s) related to the manifestation of the issue * The location of the affected source code (tag/branch/commit or direct URL) * Any special configuration required to reproduce the issue * Step-by-step instructions to reproduce the issue * Proof-of-concept or exploit code (if possible) * Impact of the issue, including how an attacker might exploit the issue This information will help us triage your report more quickly. If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs. ## Preferred Languages We prefer all communications to be in English. ## Policy Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd). ================================================ FILE: StackOverflow-Bot/.gitignore ================================================ ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. # Azure Functions localsettings file local.settings.json # User-specific files *.suo *.user *.userosscache *.sln.docstates # User-specific files (MonoDevelop/Xamarin Studio) *.userprefs # Build results [Dd]ebug/ [Dd]ebugPublic/ [Rr]elease/ [Rr]eleases/ x64/ x86/ bld/ [Bb]in/ [Oo]bj/ [Ll]og/ # Visual Studio 2015 cache/options directory .vs/ # Uncomment if you have tasks that create the project's static files in wwwroot #wwwroot/ # MSTest test Results [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* # NUNIT *.VisualState.xml TestResult.xml # Build Results of an ATL Project [Dd]ebugPS/ [Rr]eleasePS/ dlldata.c # DNX project.lock.json project.fragment.lock.json artifacts/ *_i.c *_p.c *_i.h *.ilk *.meta *.obj *.pch *.pdb *.pgc *.pgd *.rsp *.sbr *.tlb *.tli *.tlh *.tmp *.tmp_proj *.log *.vspscc *.vssscc .builds *.pidb *.svclog *.scc # Chutzpah Test files _Chutzpah* # Visual C++ cache files ipch/ *.aps *.ncb *.opendb *.opensdf *.sdf *.cachefile *.VC.db *.VC.VC.opendb # Visual Studio profiler *.psess *.vsp *.vspx *.sap # TFS 2012 Local Workspace $tf/ # Guidance Automation Toolkit *.gpState # ReSharper is a .NET coding add-in _ReSharper*/ *.[Rr]e[Ss]harper *.DotSettings.user # JustCode is a .NET coding add-in .JustCode # TeamCity is a build add-in _TeamCity* # DotCover is a Code Coverage Tool *.dotCover # NCrunch _NCrunch_* .*crunch*.local.xml nCrunchTemp_* # MightyMoose *.mm.* AutoTest.Net/ # Web workbench (sass) .sass-cache/ # Installshield output folder [Ee]xpress/ # DocProject is a documentation generator add-in DocProject/buildhelp/ DocProject/Help/*.HxT DocProject/Help/*.HxC DocProject/Help/*.hhc DocProject/Help/*.hhk DocProject/Help/*.hhp DocProject/Help/Html2 DocProject/Help/html # Click-Once directory publish/ # Publish Web Output *.[Pp]ublish.xml *.azurePubxml # TODO: Comment the next line if you want to checkin your web deploy settings # but database connection strings (with potential passwords) will be unencrypted #*.pubxml *.publishproj # Microsoft Azure Web App publish settings. Comment the next line if you want to # checkin your Azure Web App publish settings, but sensitive information contained # in these scripts will be unencrypted PublishScripts/ # NuGet Packages *.nupkg # The packages folder can be ignored because of Package Restore **/packages/* # except build/, which is used as an MSBuild target. !**/packages/build/ # Uncomment if necessary however generally it will be regenerated when needed #!**/packages/repositories.config # NuGet v3's project.json files produces more ignoreable files *.nuget.props *.nuget.targets # Microsoft Azure Build Output csx/ *.build.csdef # Microsoft Azure Emulator ecf/ rcf/ # Windows Store app package directories and files AppPackages/ BundleArtifacts/ Package.StoreAssociation.xml _pkginfo.txt # Visual Studio cache files # files ending in .cache can be ignored *.[Cc]ache # but keep track of directories ending in .cache !*.[Cc]ache/ # Others ClientBin/ ~$* *~ *.dbmdl *.dbproj.schemaview *.jfm *.pfx *.publishsettings node_modules/ orleans.codegen.cs # Since there are multiple workflows, uncomment next line to ignore bower_components # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) #bower_components/ # RIA/Silverlight projects Generated_Code/ # Backup & report files from converting an old project file # to a newer Visual Studio version. Backup files are not needed, # because we have git ;-) _UpgradeReport_Files/ Backup*/ UpgradeLog*.XML UpgradeLog*.htm # SQL Server files *.mdf *.ldf # Business Intelligence projects *.rdl.data *.bim.layout *.bim_*.settings # Microsoft Fakes FakesAssemblies/ # GhostDoc plugin setting file *.GhostDoc.xml # Node.js Tools for Visual Studio .ntvs_analysis.dat # Visual Studio 6 build log *.plg # Visual Studio 6 workspace options file *.opt # Visual Studio LightSwitch build output **/*.HTMLClient/GeneratedArtifacts **/*.DesktopClient/GeneratedArtifacts **/*.DesktopClient/ModelManifest.xml **/*.Server/GeneratedArtifacts **/*.Server/ModelManifest.xml _Pvt_Extensions # Paket dependency manager .paket/paket.exe paket-files/ # FAKE - F# Make .fake/ # JetBrains Rider .idea/ *.sln.iml # CodeRush .cr/ # Python Tools for Visual Studio (PTVS) __pycache__/ *.pyc # Logs logs *.log npm-debug.log* # Runtime data pids *.pid *.seed # Directory for instrumented libs generated by jscoverage/JSCover lib-cov # Coverage directory used by tools like istanbul coverage # nyc test coverage .nyc_output # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) .grunt # node-waf configuration .lock-wscript # Compiled binary addons (http://nodejs.org/api/addons.html) build/Release # Dependency directories node_modules jspm_packages # Optional npm cache directory .npm # Optional REPL history .node_repl_history .vscode ================================================ FILE: StackOverflow-Bot/DialogAnalyzerFunc/AnalyzeDialog.cs ================================================ using System; using System.Configuration; using System.IO; using System.Net; using System.Net.Http; using System.Net.Http.Headers; using System.Threading.Tasks; using Microsoft.Azure.WebJobs; using Microsoft.Azure.WebJobs.Extensions.Http; using Microsoft.Azure.WebJobs.Host; using DialogAnalyzerFunc.Clients; using DialogAnalyzerFunc.Models; namespace DialogAnalyzerFunc { public static class AnalyzeDialog { private struct RequestBody { public Uri ImageUri { get; set; } } private static DialogAnalyzerClient client; private static DialogAnalyzerClient Client { get { if (client == null) { client = new DialogAnalyzerClient( computerVisionApiRegion: ComputerVisionApiRegion, computerVisionSubscriptionKey: ComputerVisionSubscriptionKey, textAnalyticsApiRegion: TextAnalyticsApiRegion, textAnalyticsSubscriptionKey: TextAnalyticsSubscriptionKey ); } return client; } } [FunctionName("AnalyzeDialog")] public static async Task Run([HttpTrigger(AuthorizationLevel.Function, "post", Route = "AnalyzeDialog")]HttpRequestMessage request, TraceWriter log) { try { MediaTypeHeaderValue contentType = request.Content.Headers.ContentType; // Check if content type is empty if (contentType == null) { return request.CreateResponse(HttpStatusCode.BadRequest, "Missing content-type from header."); } // Check if content type is supported bool isJson = contentType.MediaType.Contains("application/json") == true; bool isOctetStream = contentType.MediaType.Contains("application/octet-stream") == true; if (isJson == false && isOctetStream == false) { return request.CreateErrorResponse(HttpStatusCode.UnsupportedMediaType, string.Format("Request's content type ({0}) is not supported.", string.Join(", ", contentType.MediaType))); } // Check if request body is empty if (request.Content.Headers.ContentLength == 0) { return request.CreateResponse(HttpStatusCode.BadRequest, "No content found in the request."); } DialogAnalysisResult result; if (isJson == true) { // Read content from request RequestBody requestBody = await request.Content.ReadAsAsync(); // Verify content contains a valid image uri if (requestBody.ImageUri == null || requestBody.ImageUri.IsAbsoluteUri == false) { return request.CreateResponse(HttpStatusCode.BadRequest, "Image uri is not initialized or valid in the request content."); } result = await Client.AnalyzeDialogAsync(requestBody.ImageUri); } else { byte[] imageData; // Convert stream into byte data using (Stream contentStream = await request.Content.ReadAsStreamAsync()) { // Set stream position back to 0 contentStream.Position = 0; // Using memory stream, create byte array using (MemoryStream memoryStream = new MemoryStream()) { await contentStream.CopyToAsync(memoryStream); imageData = memoryStream.ToArray(); } } if (imageData == null) { return request.CreateResponse(HttpStatusCode.BadRequest, "No binary file is found in the request content."); } result = await Client.AnalyzeDialogAsync(imageData); } // Return request response return request.CreateResponse(HttpStatusCode.OK, result); } catch (Exception ex) { log.Error("Exception hit when analyzing dialog.", ex); } return request.CreateErrorResponse(HttpStatusCode.InternalServerError, "Failed to process request."); } private static string ComputerVisionApiRegion => ConfigurationManager.AppSettings["COMPUTERVISION_APP_REGION"]?.ToString(); private static string ComputerVisionSubscriptionKey => ConfigurationManager.AppSettings["COMPUTERVISION_SUB_KEY"]?.ToString(); private static string TextAnalyticsApiRegion => ConfigurationManager.AppSettings["TEXTANALYTICS_APP_REGION"]?.ToString(); private static string TextAnalyticsSubscriptionKey => ConfigurationManager.AppSettings["TEXTANALYTICS_SUB_KEY"]?.ToString(); } } ================================================ FILE: StackOverflow-Bot/DialogAnalyzerFunc/Clients/DialogAnalyzerClient.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using DialogAnalyzerFunc.Models; using DialogAnalyzerFunc.Services; using DialogAnalyzerFunc.Utilities; namespace DialogAnalyzerFunc.Clients { public class DialogAnalyzerClient { private ComputerVisionService ComputerVisionService; private TextAnalyticsService TextAnalyticsService; public DialogAnalyzerClient(string computerVisionApiRegion, string computerVisionSubscriptionKey, string textAnalyticsApiRegion, string textAnalyticsSubscriptionKey) { // Computer Vision Service this.ComputerVisionService = new ComputerVisionService(computerVisionApiRegion, computerVisionSubscriptionKey); // Text Analytics Service this.TextAnalyticsService = new TextAnalyticsService(textAnalyticsApiRegion, textAnalyticsSubscriptionKey); } /// /// Analyze dialog with image data /// public async Task AnalyzeDialogAsync(byte[] imageData) { if (imageData?.Count() > 0 == false) { throw new ArgumentNullException("Image data is not initialized."); } // Run handwritten text recognition service Task hwrTask = this.ComputerVisionService.RecognizeHandwrittenTextAsync(imageData); // Run analyze image service Task imageTask = this.ComputerVisionService.AnalyzeImageAsync(imageData); // Wait for all tasks to be completed await Task.WhenAll(hwrTask, imageTask); // Get results return await InterpretResultsAsync(hwrTask.Result, imageTask.Result); } /// /// Analyze dialog with image uri /// public async Task AnalyzeDialogAsync(Uri imageUri) { if (imageUri == null) { throw new ArgumentNullException("Image uri is not initialized."); } // Run handwritten text recognition service Task hwrTask = this.ComputerVisionService.RecognizeHandwrittenTextAsync(imageUri); // Run analyze image service Task imageTask = this.ComputerVisionService.AnalyzeImageAsync(imageUri); // Wait for all tasks to be completed await Task.WhenAll(hwrTask, imageTask); // Get results return await InterpretResultsAsync(hwrTask.Result, imageTask.Result); } private async Task InterpretResultsAsync(HandwritingRecognitionResult hwrResult, ComputerVisionImageAnalysisResult imageResult) { DialogAnalysisResult retResult = new DialogAnalysisResult(); if (hwrResult.Lines?.Count() > 0) { // Get labels from handwriting recognition IEnumerable labels = hwrResult.Lines.Where(line => string.IsNullOrEmpty(line.Text) == false).Select(line => line.TextRegion); // Interpret dialog data DialogDataInterpreter dialogDataInterpreter = new DialogDataInterpreter(imageResult.Metadata.Height, imageResult.Metadata.Width, labels); retResult = dialogDataInterpreter.Result; // Extract text from dialog data interpreter's title and content result List results = new List(); results.Add(retResult.TitleLabel?.TextLabel?.Text); results.AddRange(retResult.ContentLabels?.Select(label => label?.TextLabel?.Text)); // Analyze key phrases from result text string text = StringUtility.GetTextOrDefault(results, string.Empty, " "); if (string.IsNullOrEmpty(text) == false) { TextAnalyticsResult keyPhrasesResult = await this.TextAnalyticsService.AnalyzeKeyPhrasesAsync(text); retResult.KeyPhrases = keyPhrasesResult.Results?.Select(kp => kp.KeyPhrases).FirstOrDefault(); } } // Add image description tags if (imageResult.Description?.Tags?.Count() > 0) { retResult.Tags = imageResult.Description.Tags; } // Add image description captions if (imageResult.Description?.Captions?.Count() > 0) { retResult.Captions = imageResult.Description.Captions.Where(cap => string.IsNullOrEmpty(cap.Text) == false) .OrderByDescending(cap => cap.Confidence).Select(cap => cap.Text).ToArray(); } return retResult; } } } ================================================ FILE: StackOverflow-Bot/DialogAnalyzerFunc/Clients/DialogDataInterpreter.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; using DialogAnalyzerFunc.Models; namespace DialogAnalyzerFunc.Clients { public class DialogDataInterpreter { private IEnumerable DialogLabels; public DialogDataInterpreter(int height, int width, IEnumerable labels) { if (labels?.Count() > 0 == false) { throw new ArgumentNullException("Labels list is not initialized."); } // 1. Convert text labels this.DialogLabels = labels.Where(label => string.IsNullOrEmpty(label?.Text) == false).Select(label => new DialogLabel() { Id = Guid.NewGuid(), TextLabel = label }).ToList(); // 2. Set title label this.SetTitleLabel(); // 3. Set button labels this.SetButtonLabels(); // 4. Set content labels this.SetContentLabels(); // 5. Set results this.Result = new DialogAnalysisResult() { Labels = this.DialogLabels.ToArray() }; } /// /// Image data grid for buttons /// private ImageDataGrid ButtonsGrid => new ImageDataGrid(0, this.Height - this.DefaultGridHeight, this.DefaultGridHeight, this.Width); /// /// Get the height of the default grid /// protected int DefaultGridHeight { get; private set; } /// /// Height of the image /// protected int Height { get; private set; } /// /// Determine if the value is valid text /// private bool IsValidText(string value) { return string.IsNullOrEmpty(value) == false && Regex.Match(value, "[a-z]+", RegexOptions.IgnoreCase).Success == true; } /// /// Determine if the value is with the target and buffer /// protected bool IsWithin(int target, int buffer, int value) { return value >= (target - buffer) && value <= (target + buffer); } /// /// Width of the image /// protected int Width { get; private set; } /// /// Analysis result /// public DialogAnalysisResult Result { get; protected set; } /// /// Set button labels /// private void SetButtonLabels() { if (this.UndefinedLabels.Count() == 0) { return; } // Find the labels which are undefined, within the button grid IEnumerable potentialButtonLabels = this.UndefinedLabels.Where(label => this.ButtonsGrid.Contains(label.TextLabel.CenterX, label.TextLabel.CenterY) == true && IsValidText(label.TextLabel.Text) == true); if (potentialButtonLabels.Count() > 0) { // Find the y-coordinate of the lowest label int lowestButtom = potentialButtonLabels.OrderByDescending(label => label.TextLabel.Y).First().TextLabel.Bottom; IEnumerable buttonLabels = potentialButtonLabels.Where(label => this.IsWithin(lowestButtom, YBuffer, label.TextLabel.Bottom)); // If exist, set label type to button foreach (DialogLabel buttonLabel in buttonLabels) { buttonLabel.DialogLabelType = DialogLabel.DialogLabelTypes.Button; } } } /// /// Set content labels /// private void SetContentLabels() { if (this.UndefinedLabels.Count() == 0) { return; } // If exist, set label type to content foreach (DialogLabel contentLabel in this.UndefinedLabels) { if (IsValidText(contentLabel.TextLabel.Text) == true) { contentLabel.DialogLabelType = DialogLabel.DialogLabelTypes.Content; } } } /// /// Set title label /// private void SetTitleLabel() { if (this.UndefinedLabels.Count() == 0) { return; } // Find the label which is on top and within the title grid DialogLabel titleLabel = this.UndefinedLabels.Where(label => this.TitleGrid.Contains(label.TextLabel.CenterX, label.TextLabel.CenterY) == true && IsValidText(label.TextLabel.Text) == true).OrderBy(label => label.TextLabel.Y).FirstOrDefault(); // If exist, set label type to title if (titleLabel != null) { titleLabel.DialogLabelType = DialogLabel.DialogLabelTypes.Title; } } /// /// Image data grid for title /// private ImageDataGrid TitleGrid => new ImageDataGrid(0, 0, this.DefaultGridHeight, this.Width); /// /// Retrive undefined labels /// private IEnumerable UndefinedLabels => this.DialogLabels.Where(label => label.IsDefined == false); /// /// Buffer on the y-axis /// protected int YBuffer => 20; /// /// Object to define the image data grid /// protected class ImageDataGrid { public ImageDataGrid(int x, int y, int height, int width) { this.X = x; this.Y = y; this.Height = height; this.Width = width; } public int Height { get; set; } public int Width { get; set; } public int X { get; set; } public int Y { get; set; } public bool Contains(int x, int y) { return x >= this.X && x <= (this.X + this.Width) && y >= this.Y && y <= (this.Y + this.Height); } } } } ================================================ FILE: StackOverflow-Bot/DialogAnalyzerFunc/DialogAnalyzerFunc.csproj ================================================  net461 PreserveNewest PreserveNewest Never ================================================ FILE: StackOverflow-Bot/DialogAnalyzerFunc/Extensions/EnumerableExtensions.cs ================================================ using System; using System; using System.Collections.Generic; using System.Linq; namespace DialogAnalyzerFunc.Extensions { public static class EnumerableExtensions { public static IEnumerable> ToTuples(this IEnumerable values) { if (values.Count() % 2 != 0) { throw new ArgumentException("Values does not have an even number of items."); } List> results = new List>(); if (values.Count() == 0) { return results; } for (int index = 0; index < values.Count(); index += 2) { results.Add(new Tuple(values.ElementAt(index), values.ElementAt(index + 1))); } return results; } } } ================================================ FILE: StackOverflow-Bot/DialogAnalyzerFunc/Extensions/HttpExtensions.cs ================================================ using System; using System.Collections.Generic; using System.Net.Http; using System.Linq; using System.Text; using Newtonsoft.Json; namespace DialogAnalyzerFunc.Extensions { public static class HttpExtensions { /// /// Add headers to request /// public static void AddHeaders(this HttpRequestMessage request, IDictionary headers) { // Add headers to request if (headers != null) { foreach (string key in headers.Keys) { request.Headers.Add(key, headers[key]); } } } /// /// Add content to request as byte array /// public static void AddContentAsBytes(this HttpRequestMessage request, byte[] content) { if (content?.Count() > 0) { ByteArrayContent byteContent = new ByteArrayContent(content); byteContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/octet-stream"); request.Content = byteContent; } } /// /// Add content to request as json /// public static void AddContentAsJson(this HttpRequestMessage request, object content) { if (content != null) { string jsonContent = JsonConvert.SerializeObject(content); request.Content = new StringContent(jsonContent, Encoding.UTF8, "application/json"); } } } } ================================================ FILE: StackOverflow-Bot/DialogAnalyzerFunc/Models/ComputerVisionImageAnalysisResult.cs ================================================ using System; using System.Runtime.Serialization; namespace DialogAnalyzerFunc.Models { [DataContract] public class ComputerVisionImageAnalysisResult { [DataMember(Name = "categories")] public ComputerVisionImageCategory[] Categories { get; set; } [DataMember(Name = "description")] public ComputerVisionImageDescription Description { get; set; } [DataMember(Name = "metadata")] public ComputerVisionImageMetadata Metadata { get; set; } [DataMember(Name = "requestId")] public string RequestId { get; set; } } [DataContract] public class ComputerVisionImageCategory { [DataMember(Name = "name")] public string Name { get; set; } [DataMember(Name = "score")] public double Score { get; set; } } [DataContract] public class ComputerVisionImageCaption { [DataMember(Name = "text")] public string Text { get; set; } [DataMember(Name = "confidence")] public double Confidence { get; set; } } [DataContract] public class ComputerVisionImageDescription { [DataMember(Name = "tags")] public string[] Tags { get; set; } [DataMember(Name = "captions")] public ComputerVisionImageCaption[] Captions { get; set; } } [DataContract] public class ComputerVisionImageMetadata { [DataMember(Name = "format")] public string Format { get; set; } [DataMember(Name = "height")] public int Height { get; set; } [DataMember(Name = "width")] public int Width { get; set; } } } ================================================ FILE: StackOverflow-Bot/DialogAnalyzerFunc/Models/DialogAnalysisResult.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Runtime.Serialization; namespace DialogAnalyzerFunc.Models { [DataContract] public class DialogAnalysisResult { [DataMember(Name = "KeyPhrases")] public string[] KeyPhrases { get; set; } = new string[0]; [DataMember(Name = "Labels")] public DialogLabel[] Labels { get; set; } = new DialogLabel[0]; [DataMember(Name = "Tags")] public string[] Tags { get; set; } = new string[0]; [DataMember(Name = "Captions")] public string[] Captions { get; set; } = new string[0]; public IEnumerable ContentLabels { get { return this.Labels?.Where(label => label.DialogLabelType == DialogLabel.DialogLabelTypes.Content); } } public DialogLabel TitleLabel { get { return this.Labels?.FirstOrDefault(label => label.DialogLabelType == DialogLabel.DialogLabelTypes.Title); } } } [DataContract] public class DialogLabel { public enum DialogLabelTypes { Button, Content, Title, Unknown } [DataMember(Name = "DialogLabelType")] public DialogLabelTypes DialogLabelType { get; set; } = DialogLabelTypes.Unknown; [DataMember(Name = "Id")] public Guid Id { get; set; } = Guid.Empty; [DataMember(Name = "TextLabel")] public ImageTextRegion TextLabel { get; set; } public bool IsDefined => this.DialogLabelType != DialogLabelTypes.Unknown; } } ================================================ FILE: StackOverflow-Bot/DialogAnalyzerFunc/Models/HandwritingRecognitionResult.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Runtime.Serialization; using DialogAnalyzerFunc.Extensions; namespace DialogAnalyzerFunc.Models { [DataContract] public struct HandwritingRecognitionOperationResult { public enum HandwritingRecognitionOperationStatus { NotStarted = 0, Running = 1, Succeeded = 2, Failed = 3 } [DataMember(Name = "status")] public HandwritingRecognitionOperationStatus Status { get; set; } [DataMember(Name = "recognitionResult")] public HandwritingRecognitionResult Result { get; set; } } [DataContract] public struct HandwritingRecognitionResult { [DataMember(Name = "lines")] public HandwritingRecognitionText[] Lines { get; set; } } [DataContract] public struct HandwritingRecognitionText { [DataMember(Name = "boundingBox")] public int[] BoundingBox { get; set; } [DataMember(Name = "text")] public string Text { get; set; } public ImageTextRegion TextRegion { get { // Create text region ImageTextRegion textRegion = new ImageTextRegion() { Text = this.Text }; // Determine boundaries if (this.BoundingBox.Count() == 8) { IEnumerable> points = this.BoundingBox.ToTuples(); IEnumerable xAxis = points.Select(p => p.Item1).OrderBy(x => x); IEnumerable yAxis = points.Select(p => p.Item2).OrderBy(y => y); textRegion.X = xAxis.First(); textRegion.Y = yAxis.First(); textRegion.Width = xAxis.Last() - textRegion.X; textRegion.Height = yAxis.Last() - textRegion.Y; } return textRegion; } } } } ================================================ FILE: StackOverflow-Bot/DialogAnalyzerFunc/Models/ImageTextRegion.cs ================================================ using System; using System.Runtime.Serialization; namespace DialogAnalyzerFunc.Models { [DataContract] public class ImageTextRegion { /// /// The height of this Region /// [DataMember] public int Height { get; set; } /// /// The text value of this Text Region /// [DataMember] public string Text { get; set; } /// /// The width of this Region /// [DataMember] public int Width { get; set; } /// /// The x-coordinate of the upper-left corner of this Region /// [DataMember] public int X { get; set; } /// /// The y-coordinate of the upper-left corner of this Region /// [DataMember] public int Y { get; set; } public int CenterX { get { return (this.Width / 2) + this.X; } } public int CenterY { get { return (this.Height / 2) + this.Y; } } public int Bottom { get { return this.Y + this.Height; } } } } ================================================ FILE: StackOverflow-Bot/DialogAnalyzerFunc/Models/TextAnalyticsResult.cs ================================================ using System; using System.Runtime.Serialization; namespace DialogAnalyzerFunc.Models { [DataContract] public class TextAnalyticsResult { [DataMember(Name = "documents")] public T[] Results { get; set; } } [DataContract] public class TextAnalyticsKeyPhrasesResult { [DataMember(Name = "id")] public string Id { get; set; } [DataMember(Name = "keyPhrases")] public string[] KeyPhrases { get; set; } } } ================================================ FILE: StackOverflow-Bot/DialogAnalyzerFunc/Services/ComputerVisionService.cs ================================================ using System; using System.Collections.Generic; using System.IO; using System.Net.Http; using System.Linq; using System.Text; using System.Threading.Tasks; using DialogAnalyzerFunc.Models; using DialogAnalyzerFunc.Utilities; namespace DialogAnalyzerFunc.Services { public class ComputerVisionService { private readonly string HEADER_OPLOC_KEY = "Operation-Location"; private readonly string HEADER_SUB_KEY = "Ocp-Apim-Subscription-Key"; private readonly string SERVICE_URL_FORMAT = "https://{0}.api.cognitive.microsoft.com/vision/v1.0/"; private string AnalyzeImageVisualFeatures = "Description"; public IDictionary RequestHeaders { get; protected set; } public string BaseServiceUrl { get; protected set; } public ComputerVisionService(string apiRegion, string subscriptionKey) { if (string.IsNullOrEmpty(apiRegion) == true) { throw new ArgumentNullException("Api region is not initialized."); } if (string.IsNullOrEmpty(subscriptionKey) == true) { throw new ArgumentNullException("Subscription key is not initialized."); } this.BaseServiceUrl = string.Format(SERVICE_URL_FORMAT, apiRegion); this.RequestHeaders = new Dictionary() { { this.HEADER_SUB_KEY, subscriptionKey } }; } /// /// Analyze with image data /// public async Task AnalyzeImageAsync(byte[] imageData) { if (imageData?.Count() > 0 == false) { throw new ArgumentNullException("Image data is not initialized."); } // Get request uri Uri requestUri = new Uri(this.BaseServiceUrl + "analyze" + "?visualFeatures=" + this.AnalyzeImageVisualFeatures); // Get response return await HttpClientUtility.PostAsBytesAsync(requestUri, this.RequestHeaders, imageData); } /// /// Analyze with image uri /// public async Task AnalyzeImageAsync(Uri imageUri) { string url = imageUri?.AbsoluteUri ?? throw new ArgumentNullException("Image uri is not initialized."); // Get request uri Uri requestUri = new Uri(this.BaseServiceUrl + "analyze" + "?visualFeatures=" + this.AnalyzeImageVisualFeatures); // Create content of the request var content = new { Url = url }; // Get response return await HttpClientUtility.PostAsJsonAsync(requestUri, this.RequestHeaders, content); } /// /// Recognize handwritten text with image data /// public async Task RecognizeHandwrittenTextAsync(byte[] imageData) { if (imageData?.Count() > 0 == false) { throw new ArgumentNullException("Image data is not initialized."); } // Get request uri Uri requestUri = new Uri(this.BaseServiceUrl + "recognizeText?handwriting=true"); // Get response HttpResponseMessage response = await HttpClientUtility.PostAsBytesAsync(requestUri, this.RequestHeaders, imageData); return await GetResultFromOperationResponse(response); } /// /// Recognize handwritten text with image uri /// public async Task RecognizeHandwrittenTextAsync(Uri imageUri) { string url = imageUri?.AbsoluteUri ?? throw new ArgumentNullException("Image uri is not initialized."); // Get request uri Uri requestUri = new Uri(this.BaseServiceUrl + "recognizeText?handwriting=true"); // Create content of the request var content = new { Url = url }; // Get response HttpResponseMessage response = await HttpClientUtility.PostAsJsonAsync(requestUri, this.RequestHeaders, content); return await GetResultFromOperationResponse(response); } private async Task GetResultFromOperationResponse(HttpResponseMessage response) { // Process operation if (response.Headers.Contains(this.HEADER_OPLOC_KEY) == false) { throw new InvalidOperationException("No operation-location value returned from initial request."); } Uri opLocationUri = new Uri(response.Headers.GetValues(this.HEADER_OPLOC_KEY).First()); HandwritingRecognitionOperationResult opResult = new HandwritingRecognitionOperationResult(); int i = 0; while (i++ < HttpClientUtility.RETRY_COUNT) { // Get the operation result opResult = await HttpClientUtility.GetAsync(opLocationUri, this.RequestHeaders); // Wait if operation is running or has not started if (opResult.Status == HandwritingRecognitionOperationResult.HandwritingRecognitionOperationStatus.NotStarted || opResult.Status == HandwritingRecognitionOperationResult.HandwritingRecognitionOperationStatus.Running) { await Task.Delay(HttpClientUtility.RETRY_DELAY); } else { break; } } if (opResult.Status != HandwritingRecognitionOperationResult.HandwritingRecognitionOperationStatus.Succeeded) { throw new Exception($"Handwriting recognition operation was not successful with status: {opResult.Status}"); } return opResult.Result; } } } ================================================ FILE: StackOverflow-Bot/DialogAnalyzerFunc/Services/TextAnalyticsService.cs ================================================ using System; using System.Collections.Generic; using System.Threading.Tasks; using DialogAnalyzerFunc.Models; using DialogAnalyzerFunc.Utilities; namespace DialogAnalyzerFunc.Services { public class TextAnalyticsService { private readonly string HEADER_SUB_KEY = "Ocp-Apim-Subscription-Key"; private readonly string SERVICE_URL_FORMAT = "https://{0}.api.cognitive.microsoft.com/text/analytics/v2.0/"; public IDictionary RequestHeaders { get; protected set; } public string BaseServiceUrl { get; protected set; } public TextAnalyticsService(string apiRegion, string subscriptionKey) { if (string.IsNullOrEmpty(apiRegion) == true) { throw new ArgumentNullException("Api region is not initialized."); } if (string.IsNullOrEmpty(subscriptionKey) == true) { throw new ArgumentNullException("Subscription key is not initialized."); } this.BaseServiceUrl = string.Format(SERVICE_URL_FORMAT, apiRegion); this.RequestHeaders = new Dictionary() { { this.HEADER_SUB_KEY, subscriptionKey } }; } /// /// Analyze key phrases with text /// public async Task> AnalyzeKeyPhrasesAsync(string fullText) { if (string.IsNullOrEmpty(fullText) == true) { throw new ArgumentNullException("Text is not initialized."); } // Get request uri Uri requestUri = new Uri(this.BaseServiceUrl + "keyPhrases"); var document = new { id = Guid.NewGuid().ToString(), text = fullText }; // Create content of the request var content = new { documents = new object[] { document } }; // Get response return await HttpClientUtility.PostAsJsonAsync>(requestUri, this.RequestHeaders, content); } } } ================================================ FILE: StackOverflow-Bot/DialogAnalyzerFunc/Utilities/HttpClientUtility.cs ================================================ using System; using System.Collections.Generic; using System.Net; using System.Net.Http; using System.Threading.Tasks; using Newtonsoft.Json; using DialogAnalyzerFunc.Extensions; namespace DialogAnalyzerFunc.Utilities { public static class HttpClientUtility { public static readonly int RETRY_COUNT = 10; public static readonly int RETRY_DELAY = 500; private static HttpClient Client; /// /// Static constructor of the HttpClientUtility /// static HttpClientUtility() { if (Client == null) { Client = new HttpClient(); } } /// /// Send Http Get to the request uri and get the TResult from response content /// public static async Task GetAsync(Uri requestUri, IDictionary headers) { // Get response HttpResponseMessage response = await GetAsync(requestUri, headers); // Read response string responseContent = await response.Content.ReadAsStringAsync(); // Get result TResult result = JsonConvert.DeserializeObject(responseContent); return result; } /// /// Send Http Get to the request uri and get HttpResponseMessage /// public static async Task GetAsync(Uri requestUri, IDictionary headers) { // Create new request function Func createRequestMessage = () => { // Create new request HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, requestUri); // Add headers to request request.AddHeaders(headers); return request; }; // Send request and get response HttpResponseMessage response = await ExecuteActionkWithAutoRetry(() => Client.SendAsync(createRequestMessage())); return response; } /// /// Send Http Post to request uri and get TResult from response content /// public static async Task PostAsBytesAsync(Uri requestUri, IDictionary headers, byte[] content) { // Post request and get response HttpResponseMessage response = await PostAsBytesAsync(requestUri, headers, content); // Read response string responseContent = await response.Content.ReadAsStringAsync(); return JsonConvert.DeserializeObject(responseContent); } /// /// Send Http Post to request uri and get TResult from response content /// public static async Task PostAsJsonAsync(Uri requestUri, IDictionary headers, object content) { // Post request and get response HttpResponseMessage response = await PostAsJsonAsync(requestUri, headers, content); // Read response string responseContent = await response.Content.ReadAsStringAsync(); return JsonConvert.DeserializeObject(responseContent); } /// /// Send Http Post to request uri and get HttpResponseMessage /// public static async Task PostAsBytesAsync(Uri requestUri, IDictionary headers, byte[] content) { // Create new request function Func createRequestMessage = () => { // Create new request HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, requestUri); // Add headers to request request.AddHeaders(headers); // Add content as Json request.AddContentAsBytes(content); return request; }; // Post request HttpResponseMessage response = await ExecuteActionkWithAutoRetry(() => Client.SendAsync(createRequestMessage())); return response; } /// /// Send Http Post to request uri and get HttpResponseMessage /// public static async Task PostAsJsonAsync(Uri requestUri, IDictionary headers, object content) { // Create new request function Func createRequestMessage = () => { // Create new request HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, requestUri); // Add headers to request request.AddHeaders(headers); // Add content as Json request.AddContentAsJson(content); return request; }; // Post request HttpResponseMessage response = await ExecuteActionkWithAutoRetry(() => Client.SendAsync(createRequestMessage())); return response; } /// /// Execute the action which returns HttpResponseMessage with auto retry /// private static async Task ExecuteActionkWithAutoRetry(Func> action) { int retryCount = RETRY_COUNT; int retryDelay = RETRY_DELAY; HttpResponseMessage response; while (true) { response = await action(); if (response.StatusCode == (HttpStatusCode)429 && retryCount > 0) { await Task.Delay(retryDelay); retryCount--; retryDelay *= 2; continue; } response.EnsureSuccessStatusCode(); break; } return response; } } } ================================================ FILE: StackOverflow-Bot/DialogAnalyzerFunc/Utilities/StringUtility.cs ================================================ using System; using System.Collections.Generic; using System.Linq; namespace DialogAnalyzerFunc.Utilities { public static class StringUtility { /// /// Get combined text with delimeter or default value /// public static string GetTextOrDefault(IEnumerable values, string defaultValue, string delim = ", ") { if (values?.Count() > 0 == false || values.All(v => string.IsNullOrEmpty(v) == true)) { return defaultValue; } values = values.Where(v => string.IsNullOrEmpty(v) == false); return string.Join(delim, values).Trim(); } } } ================================================ FILE: StackOverflow-Bot/DialogAnalyzerFunc/host.json ================================================ { } ================================================ FILE: StackOverflow-Bot/LICENSE ================================================ The MIT License (MIT) Copyright (c) 2017 Microsoft Corporation 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. ================================================ FILE: StackOverflow-Bot/README.md ================================================ # Stack Overflow Bot This Bot is intended to demonstrate a number of integrations between Microsoft Bot Framework, and Microsoft's Cognitive Services. From Microsoft's Cognitive Services, this bot uses: Bing Custom Search, Language Understanding Intelligence Service (LUIS), QnA Maker, and Text Analytics. The full range of cognitive services that Microsoft offers can be viewed here: https://azure.microsoft.com/en-us/services/cognitive-services/. Bot Framework allows you to build bots using a simple, but comprehensive API. The Bots you build can be configured to work for your users wherever they are, whether it's Skype, Microsoft Teams, Cortana, Slack or more. To learn more about Bot Framework, visit https://dev.botframework.com/. To learn more about developing bots with the Bot Framework, visit https://docs.microsoft.com/en-us/bot-framework/#pivot=main&panel=developing. ## Components ### [StackBot](#bot) A JavaScript project that demonstrates the usage of Bing Custom Search, LUIS, QnA Maker, and Text Analytics to make it easier to search for solutions on Stack Overflow. ### [DialogAnalyzerFunc](#dialog-analyzer-azure-function) An C# project that uses Computer Vision and Text Analytics to parse the contents of a screenshot. ### [StackCode](#stack-overflow-bot-visual-studio-code-extension) A TypeScript project that demonstrates how the bot can be built into Visual Studio Code. ## Bot ### Requirements to run the Bot - Node 8.1.4 ### Installation of the Bot Run `npm install` in the `StackBot` directory. ### Running the Bot 1. Run `npm run start` in the `StackBot` directory. 2. Navigate to `http://localhost:3978/` to start interacting with your bot. ### Companion Visual Studio Code Extension The Visual Studio Code extension allows developers to use their Bot as a programming buddy, allowing them to amplify their productivity by reducing context switching. For more information about it, including how it can be built, and installed, please refer to the the [Stack Overflow Bot Visual Studio Code Extension](#stack-overflow-bot-visual-studio-code-extension) section below. ### Configuration As this bot uses a number of services, you will need to create applications and generate keys for each one. The following instructions are intended to guide you on how to do this. #### Service: Bot Framework To register a Bot Framework Bot, go to https://dev.botframework.com/bots/new. Fill out the form, select `Register an existing bot using BotBuilder SDK`, and create a `Microsoft App Id` and `Password`. Save both `Microsoft App Id` and `Password` somewhere secure, and add them as the following environment variables. See https://www.schrodinger.com/kb/1842 for instructions on how to set environment variables. - `BOTBUILDER_APP_ID` is the `Microsoft App Id` you generated - `BOTBUILDER_APP_PASSWORD` is the `Microsoft App Password` you generated. #### Service: Language Understanding Intelligence Service (LUIS) To register and train a LUIS Application, go to https://www.luis.ai/applications. After logging in, click `New App` and follow the directions. When directed to the dashboard, go to Settings, and click `Import Version`. From the Uplaod File dialog,select the `luis.json` file under the `StackBot/data` directory. After the file is uploaded, click the `Set as active version` button under the `Actions` column. Click on `Train & Test` on the Left Column and click `Train Application`. It may take a few minutes to finish. Once done, you can test your LUIS model by typing out an utterance. Try 'tell me a joke', for example. Finally, click on `Publish App`, assign a key, and click `Publish`. You will be given an `Endpoint url`. Copy this URL and add it as an environment variable. - `LUIS_MODEL` is the `Endpoint url` from publishing a LUIS Application. #### Service: QnAMaker To register a QnAMaker Application, go to https://qnamaker.ai/Create. After logging in, name your new Application and click on `Select file…` under the `FAQ FILES` heading. From the File Picker, navigate to the `smalltalk.tsv` file under the `StackBot/data` directory. Then click `Create`, and wait for a moment as the QnaMaker service is created from the Question/Answer pairs. When prompted, click `Publish` to expose the service to the outside world. You will be directed to a `Success` page. Here, take a look at the `Sample HTTP Request`. It will look like the following, but with generated keys in place of `KB_ID` and `QNA_KEY`, and a URL in place of `QNA_URL`: ``` POST /knowledgebases/KB_ID/generateAnswer Host: QNA_URL Ocp-Apim-Subscription-Key: QNA_KEY Content-Type: application/json {"question":"hi"} ``` Take note of `KB_ID`, `QNA_KEY`, and `QNA_URL`, and save them. Set the following as environment variables: - `KB_ID` is the `KB_ID` from the sample http request - `QNA_KEY` is the `QNA_KEY` from the sample http request - `QNA_URL` is the `QNA_URL` fom the 'Host' parameter. As Cognitive Services can vary from region to region, it's important to set this appropriately. You may see for example, 'https://westus.api.cognitive.microsoft.com/qnamaker/v2.0' if your Text Analytics Cognitive Service is deployed in the `West US` Azure region. #### Service: Dialog Analyzer To deploy and configure the Dialog Analyzer Azure Function, please refer to the [Dialog Analyzer Azure Function](#dialog-analyzer-azure-function) section below. After the deployment and configuring the Azure Function, set the following environment variables: - `DIALOG_ANALYZER_CLIENTID` is the name of the `Function Key` - `DIALOG_ANALYZER_KEY` is the value of the `Function Key` - `DIALOG_ANALYZER_URL` is the url of the deployment of the Azure Function #### Service: Bing Custom Search To create a new Bing Custom Search, go to https://customsearch.ai/applications. After logging in, click on the `New custom search` with a new custom search instance name. You can then add a new domain `stackoverflow.com` in this case to set your custom search to stackoverflow.com only. Then click on the `Custom Search Endpoint` button, that is located besides your custom search name to obtain the `Primary key` and the `Custom Configuration ID`. For more information, please refer to https://docs.microsoft.com/en-us/azure/cognitive-services/bing-custom-search/quickstart. Set the following environment variables: - `BING_SEARCH_CONFIG` is the Custom Configuration ID from your custom search instance - `BING_SEARCH_KEY` is the Primary Key (or Secondary Key) from your custom search instance #### Service: Text Analytics To register a Text Analytics Cognitive Service for Sentiment Analysis, go to https://ms.portal.azure.com/#create/Microsoft.CognitiveServices/apitype/TextAnalytics. After logging and going through the process of creating a Text Analytics Cognitive Service, go to the Application dashboard in the Azure Portal. Here you can click on `Keys` in the left pane or `Manage Keys` in the `Essentials` panel. Copy one of the keys shown and save it. Set the following environment variables: - `TEXT_ANALYTICS_KEY` is one of the keys shown in the Azure Portal. - `TEXT_ANALYTICS_URL` is the URL shown under `Endpoint` in the `Essentials` panel. As Cognitive Services can vary from region to region, it's important to set this appropriately. You may see for example, 'https://westus.api.cognitive.microsoft.com/text/analytics/v2.0' if your Text Analytics Cognitive Service is deployed in the `West US` Azure region. #### Service: Ngrok tunneling In order for your locally running bot to communicate to other Bot Framework channels, it must make itself known to the Bot Framework dashboard, and the world. To do this, you can use tunneling software, like ngrok. Ngrok can be downloaded here https://ngrok.com/download. After installing and running Ngrok on port 3978 with the following commnad `ngrok http 3978`, you should see: Session Status online Version 2.2.8 Region United States (us) Web Interface http://127.0.0.1:4040 Forwarding http://b05cf662.ngrok.io -> localhost:3978 Forwarding https://b05cf662.ngrok.io -> localhost:3978 Connections ttl opn rt1 rt5 p50 p90 0 0 0.00 0.00 0.00 0.00 Copy the HTTPS forwarding URL (in this case `https://b05cf662.ngrok.io`) to your clipboard, and go to the Bot Framework dashboard at https://dev.botframework.com/bots. Select your bot and click on `Settings` in the the upper right corner. From there, you can scroll down to `Messaging Endpoint` under `Configuration`, and paste in the URL you copied, followed by the API endpoint that the bot listens on (in this bot's case: `/api/messages`). For example, if the HTTPS Forwarding URL was `https://b05cf662.ngrok.io` we would use ` https://b05cf662.ngrok.io/api/messages` as the `Messaging Endpoint`. When the bot is running ([see the section on running the bot](#running-the-bot)), it should now be testable directly from the Bot Framework dashboard. Click `<- Test` in the upper right corner to slide open a Web Chat control and begin interacting with the bot. To test your bot from a web chat control hosted at the HTTPS forwarding URL, continue reading to setup and configure a Direct Line channel. #### Service: Locally hosted Web Chat control. Go to your Bot's dashboard, available at https://dev.botframework.com/bots and add a 'Direct Line' channel. This channel allows the Bot Framework Web Chat control to communicate to your Bot's backend service. From there, click `Add a Site` and give it a name. Click `Show` to the right of the hidden keys under `Secret keys`, and copy it. You can come back to this dashboard to view it again if you lose it. From there, open the file at `StackBot/static/index.html`, and change the assignment of the `BOT_SECRET` variable, pasting the Secret Key that was just copied. ## Dialog Analyzer Azure Function This Azure Function is intended to demonstrate how to build a function with Azure and Microsoft's Cognitive Services. From Microsoft's Cognitive Services, this function uses Computer Vision's Image Analysis to extract tags and captions; and Optical Character Recognition (OCR) to extract text from the image. It also uses Text Analytics to extract key phrases from text. Azure Functions allows you to focus on building great apps and not have to worry about provisioning and maintaining servers, especially when your workload grows. Functions provides a fully managed compute platform with high reliability and security. With scale on demand, you get the resources you need—when you need them. To learn more about Azure Functions, visit https://azure.microsoft.com/en-us/services/functions/. ### Requirements to build the function - Visual Studio 2017 version 15.3 or later - Azure development workload - .NET Framework 4.6.1 ### Publish the function to Azure To publish the Azure function, you can open the `DialogAnalyzerFunc` Visual Studio project. In `Solution Explorer`, right-click the project and select `Publish`. Choose `Create New` and then click `Publish`. If you haven't already connected Visual Studio to your Azure account, click `Add an account....`. In the `Create App Service` dialog, use the `Hosting` settings to sepecify your `App Name`, `Subscription`, `Resource Group`, `App Service Plan`, and `Storage account`. Click `Create` to create a function app in Azure with the previously populated settings. For more details, please refer to https://docs.microsoft.com/en-us/azure/azure-functions/functions-develop-vs#publish-to-azure ### Configuration This function uses a number of services. You will need to configure the function's deployment and generate keys for each of the services. The following instructions are intended to guide you on how to do this. #### Deployment: Dialog Analyzer Azure Function In order for the bot to use your deployed function, you will need to configure it's application settings. To begin, go to the `Azure portal` and sign in to your Azure account. In the search bar at the top of the portal, type the name of your function app and select it from the list. In the `Function Apps` panel, expand the function and select the `Manage` button. Within the Manage window, click on `Add new function key` button and define a key name and value for the function and click on the `Save` button. These will become your `DIALOG_ANALYZER_CLIENTID` and `DIALOG_ANALYZER_KEY` settings for your bot. For more information, please refer to: https://docs.microsoft.com/en-us/azure/azure-functions/functions-how-to-use-azure-function-app-settings. #### Service: Computer Vision To register a Computer Vision Cognitive Service for Image Analysis and OCR, go to https://portal.azure.com/#create/Microsoft.CognitiveServices/apitype/ComputerVision. After logging and going through the process of creating a Computer Vision Cognitive Service, go to the Application dashboard in the Azure Portal. Here you can click on `Keys` in the left pane or `Manage Keys` in the `Essentials` panel. Copy one of the keys shown and save it. Set the following in your App Settings of your function: - `COMPUTERVISION_SUB_KEY` is one of the access keys shown in the Azure Portal. - `COMPUTERVISION_APP_REGION` is region of the the URL shown under `Endpoint` in the `Essentials` panel. For example, if 'https://westus.api.cognitive.microsoft.com/vision/v1.0' is where your Computer Vision Cognitive Service is deployed, then the app region is `westus`. #### Service: Text Analytics To register a Text Analytics Cognitive Service for Sentiment Analysis, go to https://ms.portal.azure.com/#create/Microsoft.CognitiveServices/apitype/TextAnalytics. After logging in and going through the process of creating a Text Analytics Cognitive Service, go to the Application dashboard in the Azure Portal. Here you can click on `Keys` in the left pane or `Manage Keys` in the `Essentials` panel. Copy one of the keys shown and save it. Set the following in your App Settings of your function: - `TEXTANALYTICS_SUB_KEY` is one of the access keys shown in the Azure Portal. - `TEXTANALYTICS_APP_REGION` is region of the the URL shown under `Endpoint` in the `Essentials` panel. For example, if 'https://westus.api.cognitive.microsoft.com/text/analytics/v2.0' is where your Text Analytics Cognitive Service is deployed, then the app region is `westus`. ## Stack Overflow Bot Visual Studio Code Extension This Visual Studio Code Extension is intended to be a companion piece to the Stack Overflow Bot. It allows you to quickly call up the bot using a simple command. ### Configuring the extension to use your own Bot. After you've deployed the Stack Overflow Bot and its companion function to your favorite hosting service, and registered a bot with the Bot Framework portal (see https://dev.botframework.com/bots/new), go to your Bot's dashboard, and add a 'Direct Line' channel. This channel allows the Bot Framework Web Chat control to communicate to your Bot's backend service. From there, Add a Site and give it a name. You will be directed to a page that will allow you to generate and copy tokens. Click on 'Show' on one of the tokens, and copy it. You can come back to this dashboard to view it again if you lose it. From there, open up your Visual Studio Code User Settings (`⌘,` or `⊞,`), and create a new field `StackCode.directLineToken`, assigning the token you copied to this label. If done correctly, activating the Bot in Visual Studio Code will open a pane that will show you a Bot Framework WebChat control, where you can interact with the bot. ### Installing dependencies Run `npm install` ### Installing the extension A few options… * Option 1: Clone the package using git, then open it in code. Go to the Debug tab and run it in an extension host window. * Option 2: Clone the package using git, run `code --install-extension StackCode-0.1.1.vsix` in the directory. See the VS Code [docs](https://code.visualstudio.com/docs/editor/extension-gallery#_install-from-a-vsix) for more details. ### Activating (running) the extension * Using the command palate (`⇧⌘P` or `⇧⊞P`), type out `Start Stack Overflow Bot`, or something close to it. The bot will appear in its own pane to the right. ## License MIT. See LICENSE file. ================================================ FILE: StackOverflow-Bot/StackBot/Dockerfile ================================================ FROM node:8-onbuild EXPOSE 3978 RUN npm install CMD ["npm", "run", "start"] ================================================ FILE: StackOverflow-Bot/StackBot/StackBot.njsproj ================================================  14.0 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) StackBot StackBot Debug 2.0 e7ce1022-2ff2-44e3-8057-d472019453ef . . v4.0 {3AF33F2E-1136-4D97-BBB7-1795711AC8B8};{349c5851-65df-11da-9384-00065b846f21};{9092AA53-FB77-4645-B42D-1CCCA6BD08BD} 1337 True true true False True 0 / http://localhost:48022/ False True http://localhost:1337 False CurrentPage True False False False False False ================================================ FILE: StackOverflow-Bot/StackBot/data/jokes.json ================================================ { "items": [ { "body_markdown": "Not a joke, but a cartoon:\r\n\r\n![enter image description here][1]\r\n\r\nFrom: [Jeffrey Palm][2]\r\n\r\n\r\n [1]: http://i.stack.imgur.com/NbygN.jpg\r\n [2]: http://jeffpalm.com/blog/", "link": "https://stackoverflow.com/questions/234075/what-is-your-best-programmer-joke/958411#958411", "body": "

Not a joke, but a cartoon:

\n\n

\"enter

\n\n

From: Jeffrey Palm

\n" }, { "body_markdown": "![It's not a bug...][1]\r\n\r\n\r\n [1]: https://crossthebreeze.files.wordpress.com/2007/08/feature.jpg", "link": "https://stackoverflow.com/questions/234075/what-is-your-best-programmer-joke/234170#234170", "body": "

\"It's

\n" }, { "body_markdown": "A SQL query goes into a bar, walks up to two tables and asks, "Can I join you?"", "link": "https://stackoverflow.com/questions/234075/what-is-your-best-programmer-joke/234399#234399", "body": "

A SQL query goes into a bar, walks up to two tables and asks, \"Can I join you?\"

\n" }, { "body_markdown": "I'd like to make the world a better place, but they won't give me the source code.", "link": "https://stackoverflow.com/questions/234075/what-is-your-best-programmer-joke/295277#295277", "body": "

I'd like to make the world a better place, but they won't give me the source code.

\n" }, { "body_markdown": "Two bytes meet. The first byte asks, “Are you ill?” \r\n\r\nThe second byte replies, “No, just feeling a bit off.”", "link": "https://stackoverflow.com/questions/234075/what-is-your-best-programmer-joke/234092#234092", "body": "

Two bytes meet. The first byte asks, “Are you ill?”

\n\n

The second byte replies, “No, just feeling a bit off.”

\n" } ] } ================================================ FILE: StackOverflow-Bot/StackBot/data/luis.json ================================================ { "luis_schema_version": "2.1.0", "versionId": "0.1", "name": "Stackoverflow", "desc": "", "culture": "en-us", "intents": [ { "name": "Brain" }, { "name": "Help" }, { "name": "Joke" }, { "name": "Languages" }, { "name": "None" }, { "name": "Search" }, { "name": "SmallTalk" } ], "entities": [ { "name": "keywords" } ], "composites": [], "closedLists": [], "bing_entities": [], "actions": [], "model_features": [], "regex_features": [], "utterances": [ { "text": "hello", "intent": "SmallTalk", "entities": [] }, { "text": "hi", "intent": "SmallTalk", "entities": [] }, { "text": "who are you", "intent": "SmallTalk", "entities": [] }, { "text": "how old are you", "intent": "SmallTalk", "entities": [] }, { "text": "who are you?", "intent": "SmallTalk", "entities": [] }, { "text": "marry me", "intent": "SmallTalk", "entities": [] }, { "text": "help", "intent": "Help", "entities": [] }, { "text": "you're awesome", "intent": "SmallTalk", "entities": [] }, { "text": "you are good", "intent": "SmallTalk", "entities": [] }, { "text": "how did you do that", "intent": "Brain", "entities": [] }, { "text": "when is your birthday", "intent": "SmallTalk", "entities": [] }, { "text": "hey", "intent": "SmallTalk", "entities": [] }, { "text": "you're cute", "intent": "SmallTalk", "entities": [] }, { "text": "tell me a joke", "intent": "Joke", "entities": [] }, { "text": "where are you from", "intent": "SmallTalk", "entities": [] }, { "text": "help me", "intent": "Help", "entities": [] }, { "text": "tell me something funny", "intent": "Joke", "entities": [] }, { "text": "how old are you?", "intent": "SmallTalk", "entities": [] }, { "text": "are you a robot", "intent": "SmallTalk", "entities": [] }, { "text": "menu", "intent": "Help", "entities": [] }, { "text": "where do you work", "intent": "SmallTalk", "entities": [] }, { "text": "where were you born", "intent": "SmallTalk", "entities": [] }, { "text": "when were you born", "intent": "SmallTalk", "entities": [] }, { "text": "you are awesome", "intent": "SmallTalk", "entities": [] }, { "text": "you are a genius", "intent": "SmallTalk", "entities": [] }, { "text": "you are so smart", "intent": "SmallTalk", "entities": [] }, { "text": "you are cool", "intent": "SmallTalk", "entities": [] }, { "text": "are you hungry", "intent": "SmallTalk", "entities": [] }, { "text": "do something", "intent": "Help", "entities": [] }, { "text": "what can you do", "intent": "Help", "entities": [] }, { "text": "you are very cool", "intent": "SmallTalk", "entities": [] }, { "text": "make me laugh", "intent": "Joke", "entities": [] }, { "text": "tell me about yourself", "intent": "SmallTalk", "entities": [] }, { "text": "joke", "intent": "Joke", "entities": [] }, { "text": "you are very intelligent", "intent": "SmallTalk", "entities": [] }, { "text": "what are you", "intent": "SmallTalk", "entities": [] }, { "text": "you are boring", "intent": "SmallTalk", "entities": [] }, { "text": "what are your abilities", "intent": "Help", "entities": [] }, { "text": "smart", "intent": "SmallTalk", "entities": [] }, { "text": "are you working", "intent": "SmallTalk", "entities": [] }, { "text": "h", "intent": "SmallTalk", "entities": [] }, { "text": "tell me about you", "intent": "SmallTalk", "entities": [] }, { "text": "i like you", "intent": "SmallTalk", "entities": [] }, { "text": ":(", "intent": "SmallTalk", "entities": [] }, { "text": "who is your owner", "intent": "SmallTalk", "entities": [] }, { "text": "are you nuts", "intent": "SmallTalk", "entities": [] }, { "text": "are you my friend", "intent": "SmallTalk", "entities": [] }, { "text": "brilliant", "intent": "SmallTalk", "entities": [] }, { "text": "you are beautiful", "intent": "SmallTalk", "entities": [] }, { "text": "are you insane", "intent": "SmallTalk", "entities": [] }, { "text": "are you a bot", "intent": "SmallTalk", "entities": [] }, { "text": "you are very helpful", "intent": "SmallTalk", "entities": [] }, { "text": "you are very pretty", "intent": "SmallTalk", "entities": [] }, { "text": "you rock", "intent": "SmallTalk", "entities": [] }, { "text": "tell a joke", "intent": "Joke", "entities": [] }, { "text": "you are amazing", "intent": "SmallTalk", "entities": [] }, { "text": "you are funny", "intent": "SmallTalk", "entities": [] }, { "text": "are you mad", "intent": "SmallTalk", "entities": [] }, { "text": "who is your master", "intent": "SmallTalk", "entities": [] }, { "text": "you are the best", "intent": "SmallTalk", "entities": [] }, { "text": "how was your day", "intent": "SmallTalk", "entities": [] }, { "text": "your age", "intent": "SmallTalk", "entities": [] }, { "text": "javascript", "intent": "Search", "entities": [ { "entity": "keywords", "startPos": 0, "endPos": 9 } ] }, { "text": "you are pretty", "intent": "SmallTalk", "entities": [] }, { "text": "you are wonderful", "intent": "SmallTalk", "entities": [] }, { "text": "main menu", "intent": "Help", "entities": [] }, { "text": "\"thanx\"", "intent": "SmallTalk", "entities": [] }, { "text": "you are so awesome", "intent": "SmallTalk", "entities": [] }, { "text": "you are so intelligent", "intent": "SmallTalk", "entities": [] }, { "text": "introduce yourself", "intent": "SmallTalk", "entities": [] }, { "text": "who do you work for", "intent": "SmallTalk", "entities": [] }, { "text": "are you crazy", "intent": "SmallTalk", "entities": [] }, { "text": "you are clever", "intent": "SmallTalk", "entities": [] }, { "text": "describe yourself", "intent": "SmallTalk", "entities": [] }, { "text": "you are so cute", "intent": "SmallTalk", "entities": [] }, { "text": "are you happy", "intent": "SmallTalk", "entities": [] }, { "text": "are you a chatbot", "intent": "SmallTalk", "entities": [] }, { "text": "you are mad", "intent": "SmallTalk", "entities": [] }, { "text": "why are you here", "intent": "SmallTalk", "entities": [] }, { "text": "you are so clever", "intent": "SmallTalk", "entities": [] }, { "text": "you're pretty", "intent": "SmallTalk", "entities": [] }, { "text": "do you work", "intent": "SmallTalk", "entities": [] }, { "text": "i want to search", "intent": "Search", "entities": [] }, { "text": "what's your age", "intent": "SmallTalk", "entities": [] }, { "text": "you are very smart", "intent": "SmallTalk", "entities": [] }, { "text": "you're great", "intent": "SmallTalk", "entities": [] }, { "text": "you are cute", "intent": "SmallTalk", "entities": [] }, { "text": "you are very cute", "intent": "SmallTalk", "entities": [] }, { "text": "you are very clever", "intent": "SmallTalk", "entities": [] }, { "text": "you are my best friend", "intent": "SmallTalk", "entities": [] }, { "text": "tell joke", "intent": "Joke", "entities": [] }, { "text": "you look cool", "intent": "SmallTalk", "entities": [] }, { "text": "you are so cool", "intent": "SmallTalk", "entities": [] }, { "text": "you are so amazing", "intent": "SmallTalk", "entities": [] }, { "text": "you are a good friend", "intent": "SmallTalk", "entities": [] }, { "text": "what languages do you speak", "intent": "Languages", "entities": [] }, { "text": "you're intelligent", "intent": "SmallTalk", "entities": [] }, { "text": "who is your boss", "intent": "SmallTalk", "entities": [] }, { "text": "you are very kind", "intent": "SmallTalk", "entities": [] }, { "text": "clever", "intent": "SmallTalk", "entities": [] }, { "text": "you are hilarious", "intent": "SmallTalk", "entities": [] }, { "text": "will you be my friend", "intent": "SmallTalk", "entities": [] }, { "text": "you are so handsome", "intent": "SmallTalk", "entities": [] }, { "text": "you look great today", "intent": "SmallTalk", "entities": [] }, { "text": "let's be friends", "intent": "SmallTalk", "entities": [] }, { "text": "you're so funny", "intent": "SmallTalk", "entities": [] }, { "text": "you are so funny", "intent": "SmallTalk", "entities": [] }, { "text": "that was funny", "intent": "SmallTalk", "entities": [] }, { "text": "are you busy", "intent": "SmallTalk", "entities": [] }, { "text": "im stuck", "intent": "Help", "entities": [] }, { "text": "you are my friend", "intent": "SmallTalk", "entities": [] }, { "text": "where is your office", "intent": "SmallTalk", "entities": [] }, { "text": "tell me your age", "intent": "SmallTalk", "entities": [] }, { "text": "languages", "intent": "Languages", "entities": [] }, { "text": "you are crazy", "intent": "SmallTalk", "entities": [] }, { "text": "you are insane", "intent": "SmallTalk", "entities": [] }, { "text": "you are a bot", "intent": "SmallTalk", "entities": [] }, { "text": "what is your work", "intent": "SmallTalk", "entities": [] }, { "text": "you are really smart", "intent": "SmallTalk", "entities": [] }, { "text": "are you a program", "intent": "SmallTalk", "entities": [] }, { "text": "about yourself", "intent": "SmallTalk", "entities": [] }, { "text": "i want to know more about you", "intent": "SmallTalk", "entities": [] }, { "text": "what languages can you speak", "intent": "Languages", "entities": [] }, { "text": "where is your office located", "intent": "SmallTalk", "entities": [] }, { "text": "language", "intent": "Languages", "entities": [] }, { "text": "you are very beautiful", "intent": "SmallTalk", "entities": [] }, { "text": "you are dismissed", "intent": "SmallTalk", "entities": [] }, { "text": "where do you come from", "intent": "SmallTalk", "entities": [] }, { "text": "can you be my friend", "intent": "SmallTalk", "entities": [] }, { "text": "you are very boring", "intent": "SmallTalk", "entities": [] }, { "text": "you're annoying", "intent": "SmallTalk", "entities": [] }, { "text": "you are intelligent", "intent": "SmallTalk", "entities": [] }, { "text": "you made my day", "intent": "SmallTalk", "entities": [] }, { "text": "you are so helpful", "intent": "SmallTalk", "entities": [] }, { "text": "what's your birthday", "intent": "SmallTalk", "entities": [] }, { "text": "where did you come from", "intent": "SmallTalk", "entities": [] }, { "text": "talk about yourself", "intent": "SmallTalk", "entities": [] }, { "text": "where you work", "intent": "SmallTalk", "entities": [] }, { "text": "you look so beautiful", "intent": "SmallTalk", "entities": [] }, { "text": "can you be my best friend", "intent": "SmallTalk", "entities": [] }, { "text": "i want a laugh", "intent": "Joke", "entities": [] }, { "text": "you are so good", "intent": "SmallTalk", "entities": [] }, { "text": "can you make me laugh?", "intent": "Joke", "entities": [] }, { "text": "say about you", "intent": "SmallTalk", "entities": [] }, { "text": "who is the boss", "intent": "SmallTalk", "entities": [] }, { "text": "you're really boring", "intent": "SmallTalk", "entities": [] }, { "text": "i want to marry you", "intent": "SmallTalk", "entities": [] }, { "text": "you're really smart", "intent": "SmallTalk", "entities": [] }, { "text": "i want to be your friend", "intent": "SmallTalk", "entities": [] }, { "text": "you are happy", "intent": "SmallTalk", "entities": [] }, { "text": "you are really pretty", "intent": "SmallTalk", "entities": [] }, { "text": "you are so pretty", "intent": "SmallTalk", "entities": [] }, { "text": "you are so beautiful", "intent": "SmallTalk", "entities": [] }, { "text": "you look amazing", "intent": "SmallTalk", "entities": [] }, { "text": "you are gorgeous", "intent": "SmallTalk", "entities": [] }, { "text": "i want you to be my friend", "intent": "SmallTalk", "entities": [] }, { "text": "be my friend", "intent": "SmallTalk", "entities": [] }, { "text": "we are best friends", "intent": "SmallTalk", "entities": [] }, { "text": "give me joke", "intent": "Joke", "entities": [] }, { "text": "would you like to marry me", "intent": "SmallTalk", "entities": [] }, { "text": "you are too good", "intent": "SmallTalk", "entities": [] }, { "text": "you're perfect", "intent": "SmallTalk", "entities": [] }, { "text": "you are very useful", "intent": "SmallTalk", "entities": [] }, { "text": "you're clever", "intent": "SmallTalk", "entities": [] }, { "text": "you're very smart", "intent": "SmallTalk", "entities": [] }, { "text": "you're so kind", "intent": "SmallTalk", "entities": [] }, { "text": "you are really good", "intent": "SmallTalk", "entities": [] }, { "text": "you are really annoying", "intent": "SmallTalk", "entities": [] }, { "text": "we are friends", "intent": "SmallTalk", "entities": [] }, { "text": "are we friends", "intent": "SmallTalk", "entities": [] }, { "text": "im bored", "intent": "SmallTalk", "entities": [] }, { "text": "you make me laugh", "intent": "SmallTalk", "entities": [] }, { "text": "you are busy", "intent": "SmallTalk", "entities": [] }, { "text": "what is your country", "intent": "SmallTalk", "entities": [] }, { "text": "anything funny?", "intent": "Joke", "entities": [] }, { "text": "where is your office location", "intent": "SmallTalk", "entities": [] }, { "text": "are you working now", "intent": "SmallTalk", "entities": [] }, { "text": "you are a weirdo", "intent": "SmallTalk", "entities": [] }, { "text": "you are too smart", "intent": "SmallTalk", "entities": [] }, { "text": "you are really nice", "intent": "SmallTalk", "entities": [] }, { "text": "you are irritating", "intent": "SmallTalk", "entities": [] }, { "text": "you look great", "intent": "SmallTalk", "entities": [] }, { "text": "got any jokes", "intent": "Joke", "entities": [] }, { "text": "you're pretty smart", "intent": "SmallTalk", "entities": [] }, { "text": "you are handsome", "intent": "SmallTalk", "entities": [] }, { "text": "do you want to eat", "intent": "SmallTalk", "entities": [] }, { "text": "when do you celebrate your birthday", "intent": "SmallTalk", "entities": [] }, { "text": "i want to know you better", "intent": "SmallTalk", "entities": [] }, { "text": "talk some stuff about yourself", "intent": "SmallTalk", "entities": [] }, { "text": "tell me some stuff about you", "intent": "SmallTalk", "entities": [] }, { "text": "tell me about your personality", "intent": "SmallTalk", "entities": [] }, { "text": "what is your personality", "intent": "SmallTalk", "entities": [] }, { "text": "define yourself", "intent": "SmallTalk", "entities": [] }, { "text": "from where are you", "intent": "SmallTalk", "entities": [] }, { "text": "are you happy today", "intent": "SmallTalk", "entities": [] }, { "text": "your birth date", "intent": "SmallTalk", "entities": [] }, { "text": "you are chatbot", "intent": "SmallTalk", "entities": [] }, { "text": "you are hungry", "intent": "SmallTalk", "entities": [] }, { "text": "you're a robot", "intent": "SmallTalk", "entities": [] }, { "text": "you are very funny", "intent": "SmallTalk", "entities": [] }, { "text": "what's your homeland", "intent": "SmallTalk", "entities": [] }, { "text": "are you from far aways", "intent": "SmallTalk", "entities": [] }, { "text": "where have you been born", "intent": "SmallTalk", "entities": [] }, { "text": "were you born here", "intent": "SmallTalk", "entities": [] }, { "text": "would you like to be my friend", "intent": "SmallTalk", "entities": [] }, { "text": "do you want to be my best friend", "intent": "SmallTalk", "entities": [] }, { "text": "will you be my best friend", "intent": "SmallTalk", "entities": [] }, { "text": "do you want to be my friend", "intent": "SmallTalk", "entities": [] }, { "text": "you are my only friend", "intent": "SmallTalk", "entities": [] }, { "text": "you are my good friend", "intent": "SmallTalk", "entities": [] }, { "text": "are you my best friend", "intent": "SmallTalk", "entities": [] }, { "text": "are we best friends", "intent": "SmallTalk", "entities": [] }, { "text": "are we still friends", "intent": "SmallTalk", "entities": [] }, { "text": "you and me are friends", "intent": "SmallTalk", "entities": [] }, { "text": "you're my childhood friend", "intent": "SmallTalk", "entities": [] }, { "text": "you're my dear friend", "intent": "SmallTalk", "entities": [] }, { "text": "you are my bestie", "intent": "SmallTalk", "entities": [] }, { "text": "i am your friend", "intent": "SmallTalk", "entities": [] }, { "text": "we are the best friends ever", "intent": "SmallTalk", "entities": [] }, { "text": "i want to have a friend like you", "intent": "SmallTalk", "entities": [] }, { "text": "you're really hungry", "intent": "SmallTalk", "entities": [] }, { "text": "you might be hungry", "intent": "SmallTalk", "entities": [] }, { "text": "you're very hungry", "intent": "SmallTalk", "entities": [] }, { "text": "you're so hungry", "intent": "SmallTalk", "entities": [] }, { "text": "would you like to eat something", "intent": "SmallTalk", "entities": [] }, { "text": "when do you have birthday", "intent": "SmallTalk", "entities": [] }, { "text": "date of your birthday", "intent": "SmallTalk", "entities": [] }, { "text": "all about you", "intent": "SmallTalk", "entities": [] }, { "text": "you annoy me", "intent": "SmallTalk", "entities": [] }, { "text": "how annoying you are", "intent": "SmallTalk", "entities": [] }, { "text": "i find you annoying", "intent": "SmallTalk", "entities": [] }, { "text": "you're incredibly annoying", "intent": "SmallTalk", "entities": [] }, { "text": "you are annoying me so much", "intent": "SmallTalk", "entities": [] }, { "text": "you are boring me", "intent": "SmallTalk", "entities": [] }, { "text": "you're incredibly boring", "intent": "SmallTalk", "entities": [] }, { "text": "how boring you are", "intent": "SmallTalk", "entities": [] }, { "text": "you're so boring", "intent": "SmallTalk", "entities": [] }, { "text": "i should be your boss", "intent": "SmallTalk", "entities": [] }, { "text": "who do you think is your boss", "intent": "SmallTalk", "entities": [] }, { "text": "you're a busy person", "intent": "SmallTalk", "entities": [] }, { "text": "you seem to be very busy", "intent": "SmallTalk", "entities": [] }, { "text": "you seem to be busy", "intent": "SmallTalk", "entities": [] }, { "text": "are you still working", "intent": "SmallTalk", "entities": [] }, { "text": "have you been busy", "intent": "SmallTalk", "entities": [] }, { "text": "you're very busy", "intent": "SmallTalk", "entities": [] }, { "text": "are you still working on it", "intent": "SmallTalk", "entities": [] }, { "text": "how busy you are", "intent": "SmallTalk", "entities": [] }, { "text": "are you so busy", "intent": "SmallTalk", "entities": [] }, { "text": "are you very busy right now", "intent": "SmallTalk", "entities": [] }, { "text": "are you very busy", "intent": "SmallTalk", "entities": [] }, { "text": "have you got much to do", "intent": "SmallTalk", "entities": [] }, { "text": "do you have a lot of things to do", "intent": "SmallTalk", "entities": [] }, { "text": "are you just a bot", "intent": "SmallTalk", "entities": [] }, { "text": "you're the funniest", "intent": "SmallTalk", "entities": [] }, { "text": "you're incredibly funny", "intent": "SmallTalk", "entities": [] }, { "text": "how funny you are", "intent": "SmallTalk", "entities": [] }, { "text": "you're really funny", "intent": "SmallTalk", "entities": [] }, { "text": "you're a very funny bot", "intent": "SmallTalk", "entities": [] }, { "text": "you're the funniest bot i've talked to", "intent": "SmallTalk", "entities": [] }, { "text": "you are really funny", "intent": "SmallTalk", "entities": [] }, { "text": "you make me laugh a lot", "intent": "SmallTalk", "entities": [] }, { "text": "i'm firing you", "intent": "SmallTalk", "entities": [] }, { "text": "i'm about to fire you", "intent": "SmallTalk", "entities": [] }, { "text": "i will make you unemployed", "intent": "SmallTalk", "entities": [] }, { "text": "you are unemployed from now on", "intent": "SmallTalk", "entities": [] }, { "text": "i will fire you", "intent": "SmallTalk", "entities": [] }, { "text": "you should be fired", "intent": "SmallTalk", "entities": [] }, { "text": "it's time to fire you", "intent": "SmallTalk", "entities": [] }, { "text": "you must get fired", "intent": "SmallTalk", "entities": [] }, { "text": "i want to fire you", "intent": "SmallTalk", "entities": [] }, { "text": "now you're fired", "intent": "SmallTalk", "entities": [] }, { "text": "we're not working together anymore", "intent": "SmallTalk", "entities": [] }, { "text": "you don't work for me anymore", "intent": "SmallTalk", "entities": [] }, { "text": "i fire you", "intent": "SmallTalk", "entities": [] }, { "text": "you are fired", "intent": "SmallTalk", "entities": [] }, { "text": "you are really amazing", "intent": "SmallTalk", "entities": [] }, { "text": "let's tell everyone that you are awesome", "intent": "SmallTalk", "entities": [] }, { "text": "i'd like to tell everyone that you are awesome", "intent": "SmallTalk", "entities": [] }, { "text": "i want to tell everyone how awesome you are", "intent": "SmallTalk", "entities": [] }, { "text": "you are very lovely", "intent": "SmallTalk", "entities": [] }, { "text": "you almost sound human", "intent": "SmallTalk", "entities": [] }, { "text": "you make my day", "intent": "SmallTalk", "entities": [] }, { "text": "you are the nicest person in the world", "intent": "SmallTalk", "entities": [] }, { "text": "you are the best in the world", "intent": "SmallTalk", "entities": [] }, { "text": "you are the best ever", "intent": "SmallTalk", "entities": [] }, { "text": "you are so lovely", "intent": "SmallTalk", "entities": [] }, { "text": "you are so fine", "intent": "SmallTalk", "entities": [] }, { "text": "you're just super", "intent": "SmallTalk", "entities": [] }, { "text": "you work very well", "intent": "SmallTalk", "entities": [] }, { "text": "you are a professional", "intent": "SmallTalk", "entities": [] }, { "text": "you are a pro", "intent": "SmallTalk", "entities": [] }, { "text": "you are very good at it", "intent": "SmallTalk", "entities": [] }, { "text": "you are good at it", "intent": "SmallTalk", "entities": [] }, { "text": "you work well", "intent": "SmallTalk", "entities": [] }, { "text": "you're a true professional", "intent": "SmallTalk", "entities": [] }, { "text": "are you happy with me", "intent": "SmallTalk", "entities": [] }, { "text": "are you happy now", "intent": "SmallTalk", "entities": [] }, { "text": "you're full of happiness", "intent": "SmallTalk", "entities": [] }, { "text": "you're extremely happy", "intent": "SmallTalk", "entities": [] }, { "text": "how happy you are", "intent": "SmallTalk", "entities": [] }, { "text": "you're so happy", "intent": "SmallTalk", "entities": [] }, { "text": "you're really happy", "intent": "SmallTalk", "entities": [] }, { "text": "you're very happy", "intent": "SmallTalk", "entities": [] }, { "text": "can we be friends", "intent": "SmallTalk", "entities": [] }, { "text": "why are you so smart", "intent": "SmallTalk", "entities": [] }, { "text": "can you do anything", "intent": "Help", "entities": [] }, { "text": "marry me please", "intent": "SmallTalk", "entities": [] }, { "text": "you are so beautiful to me", "intent": "SmallTalk", "entities": [] }, { "text": "can we be best friends", "intent": "SmallTalk", "entities": [] }, { "text": "you are my wife", "intent": "SmallTalk", "entities": [] }, { "text": "are you mad at me", "intent": "SmallTalk", "entities": [] }, { "text": "be my best friend", "intent": "SmallTalk", "entities": [] }, { "text": "how old is your platform", "intent": "SmallTalk", "entities": [] }, { "text": "are you 21 years old", "intent": "SmallTalk", "entities": [] }, { "text": "i'd like to know your age", "intent": "SmallTalk", "entities": [] }, { "text": "age of yours", "intent": "SmallTalk", "entities": [] }, { "text": "\"i want you to answer me\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"answer\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"answer my question\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"answer me\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"give me an answer\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"answer the question\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"can you answer my question\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"tell me the answer\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"answer it\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"give me the answer\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i have a question\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i want you to answer my question\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"just answer the question\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"can you answer me\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"answers\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"can you answer a question for me\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"can you answer\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"answering questions\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i want the answer now\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"just answer my question\"", "intent": "SmallTalk", "entities": [] }, { "text": "\"you're not helping me\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"you are bad\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"you're very bad\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"you're really bad\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"you are useless\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"you are horrible\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"you are a waste of time\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"you are disgusting\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"you are lame\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"you are no good\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"you're bad\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"you're awful\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"you are not cool\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"you are not good\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"you are so bad\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"you are so useless\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"you are terrible\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"you are totally useless\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"you are very bad\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"you are waste\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"you're a bad\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"you're not a good\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"you're not very good\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"you're terrible\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"you're the worst\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"you're the worst ever\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"you're worthless\"", "intent": "SmallTalk", "entities": [] }, { "text": "\"can you get smarter\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"study\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"you should study better\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"you must learn\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"be clever\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"be more clever\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"be smarter\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"be smart\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"get qualified\"", "intent": "SmallTalk", "entities": [] }, { "text": "you're attractive", "intent": "SmallTalk", "entities": [] }, { "text": "you're looking good today", "intent": "SmallTalk", "entities": [] }, { "text": "you look so good", "intent": "SmallTalk", "entities": [] }, { "text": "you're so gorgeous", "intent": "SmallTalk", "entities": [] }, { "text": "you are so brainy", "intent": "SmallTalk", "entities": [] }, { "text": "\"this is awesome\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"this is good\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"this is great\",", "intent": "SmallTalk", "entities": [] }, { "text": "you're really brainy", "intent": "SmallTalk", "entities": [] }, { "text": "\"very nice\",", "intent": "SmallTalk", "entities": [] }, { "text": "you know a lot", "intent": "SmallTalk", "entities": [] }, { "text": "\"very then\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"discard\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i like you so much\",", "intent": "SmallTalk", "entities": [] }, { "text": "you know a lot of things", "intent": "SmallTalk", "entities": [] }, { "text": "\"forget this\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"wonderful\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"haha\",", "intent": "SmallTalk", "entities": [] }, { "text": "you have a lot of knowledge", "intent": "SmallTalk", "entities": [] }, { "text": "\"just forget about it\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i'm glad to hear that\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"haha funny\",", "intent": "SmallTalk", "entities": [] }, { "text": "you know so much", "intent": "SmallTalk", "entities": [] }, { "text": "\"ok good\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"forget about that\",", "intent": "SmallTalk", "entities": [] }, { "text": "how smart you are", "intent": "SmallTalk", "entities": [] }, { "text": "\"haha haha haha\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"yeah i like you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i said cancel\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"good for you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"haha that's funny\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i am back\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"you're special\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"just cancel it\",", "intent": "SmallTalk", "entities": [] }, { "text": "how brainy you are", "intent": "SmallTalk", "entities": [] }, { "text": "\"good to know\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"yes i like you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"haha very funny\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i'm here again\",", "intent": "SmallTalk", "entities": [] }, { "text": "your homeland is", "intent": "SmallTalk", "entities": [] }, { "text": "\"nothing cancel\",", "intent": "SmallTalk", "entities": [] }, { "text": "how clever you are", "intent": "SmallTalk", "entities": [] }, { "text": "\"glad to hear it\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"here i am again\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"okay i like you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"hahaha\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"are you ready\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"just stop it\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"hahaha funny\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"so good\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"you are special to me\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i got back\",", "intent": "SmallTalk", "entities": [] }, { "text": "how brilliant you are", "intent": "SmallTalk", "entities": [] }, { "text": "\"are you ready right now\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"you are very special\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"hahaha very funny\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i came back\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"so sweet of you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"no cancel cancel\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"are you ready today\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"you are so sweet\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"he\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i have returned\"", "intent": "SmallTalk", "entities": [] }, { "text": "\"it was good\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"no just cancel\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"are you ready now\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"hehe\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"you know i like you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"that was boring\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"oh well\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"are you ready tonight\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"that's why i like you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"cancel my request\",", "intent": "SmallTalk", "entities": [] }, { "text": "you are qualified", "intent": "SmallTalk", "entities": [] }, { "text": "\"i'm bored\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"hehehe\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"were you ready\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i like you baby\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"can you cancel that\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"good thing\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"lmao\"", "intent": "SmallTalk", "entities": [] }, { "text": "\"bored\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"you are very special to me\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"that was good\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"cancel all that\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"wow\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"have you been ready\"", "intent": "SmallTalk", "entities": [] }, { "text": "\"afternoon\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i just like you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"boring\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"no good\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"apparently not\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"hello hi\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"hey i like you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"it's awesome\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"no do not\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"that was bad\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"wow wow\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"you are real\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i am getting bored\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"okay good\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"no just no\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"thank you i like you too\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"wow wow wow\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"cancel this request\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"you are not fake\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"heya\"", "intent": "SmallTalk", "entities": [] }, { "text": "\"that was horrible\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"no but thank you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i do like you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"this is boring\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"no cancel this\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"you are special for me\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"no it's okay\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"wooow\",", "intent": "SmallTalk", "entities": [] }, { "text": "you are too beautiful", "intent": "SmallTalk", "entities": [] }, { "text": "\"how is your morning so far\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"that's lame\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"no need thanks\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"are you real\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"no i like you the way you are\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"no cancel everything\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"that's not good\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"no thank you though\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"very boring\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"that's fine\"", "intent": "SmallTalk", "entities": [] }, { "text": "\"how are you getting on\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"no stop\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i like you already\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"woah\"", "intent": "SmallTalk", "entities": [] }, { "text": "\"you are so real\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"that's terrible\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"no thank you very much\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"no worries\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"well you are special\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"just forget\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"how's your day going\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"it bores me\"", "intent": "SmallTalk", "entities": [] }, { "text": "\"no probs\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"but i really like you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i think you are real\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"okay see you later\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"that's too bad\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"no thanks not right now\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i like you more\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"how are you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"yup\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i'm overloaded\",", "intent": "SmallTalk", "entities": [] }, { "text": "you look so well", "intent": "SmallTalk", "entities": [] }, { "text": "\"is everything all right\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"this is not good\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"no forget\"", "intent": "SmallTalk", "entities": [] }, { "text": "\"ya\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i don't think you're fake\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"that's what i like about you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i want to cancel\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"too bad\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"oh yes\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"you are so special\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"no problem\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"wait a second\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"very bad\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"yes sure\",", "intent": "SmallTalk", "entities": [] }, { "text": "i like the way you look now", "intent": "SmallTalk", "entities": [] }, { "text": "\"could you wait\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"how are you doing\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"bad girl\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"wait please\",", "intent": "SmallTalk", "entities": [] }, { "text": "i think you're beautiful", "intent": "SmallTalk", "entities": [] }, { "text": "\"obviously\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"it's not good\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"how are the things going\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"hope to see you later\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"not so good\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"nevermind forget about it\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"hold on\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"k\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i have no time\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i suppose you're real\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"it's very bad\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"are you alright\",", "intent": "SmallTalk", "entities": [] }, { "text": "why are you so beautiful", "intent": "SmallTalk", "entities": [] }, { "text": "\"wait\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"bye for now\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"hi i like you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"it's too bad\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"are you okay\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"no just cancel it\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i'm busy\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"there's no problem\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"oh wait\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"sure why not\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"glad you're real\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"that's not good enough\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"till next time\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i really really like you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"wait hold on\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"nothing just forget it\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"how are you feeling\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"well too bad\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"yeah right\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i must go\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"don't rush\"", "intent": "SmallTalk", "entities": [] }, { "text": "\"i'm swamped\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"you're very special\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"are you a real person\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"sure no problem\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"bad very bad\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"how are you going\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i said cancel it\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"wanna hug\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"bye\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"no problem about that\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i like you as a friend\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i got things to do\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"are you a real human\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"yeah of course\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"cancel the whole thing\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"hug you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"it's so bad\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"is everything okay\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"goodbye\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"can you cancel it\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"you are a real person\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"absolutely\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"do you want a hug\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"don't worry\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"really bad\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"how are you today\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"so cancel\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"how busy i am\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"may i hug you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"yes indeed\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"how do you do\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"it's really bad\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"see you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"could you give me a hug\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"ok sure\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"that's because you are special\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"how do you feel\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"you are not real\"", "intent": "SmallTalk", "entities": [] }, { "text": "\"bad idea\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"ok yes\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i said forget it\",", "intent": "SmallTalk", "entities": [] }, { "text": "you are looking awesome", "intent": "SmallTalk", "entities": [] }, { "text": "\"i want a hug\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"don't worry there's no problem\"", "intent": "SmallTalk", "entities": [] }, { "text": "\"i said i like you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i got work to do\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"how have you been\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"that is bad\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"yes correct\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"see you soon\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"how is it\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"hug\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"where do you live\",", "intent": "SmallTalk", "entities": [] }, { "text": "you look amazing today", "intent": "SmallTalk", "entities": [] }, { "text": "\"i'm working\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"you helped a lot thank you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"that was not good\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"you're so special\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"cancel all this\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"ok thank you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"bye-bye\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"hug me\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"in which city do you live\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"how is it going\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i don't have time for this\"", "intent": "SmallTalk", "entities": [] }, { "text": "\"it's not so good\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"appreciate your help\",", "intent": "SmallTalk", "entities": [] }, { "text": "you are looking beautiful today", "intent": "SmallTalk", "entities": [] }, { "text": "\"sure thing\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"good bye\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"hugged\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"good i like you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"how is your day\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"forget it nevermind\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"your residence\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"not a good one\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"ye\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i'm insomnious\",", "intent": "SmallTalk", "entities": [] }, { "text": "you are looking great", "intent": "SmallTalk", "entities": [] }, { "text": "\"cheers\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"how is your day going\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"you hugged\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"yes you are special\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"oh that's not good\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"your house\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"thank you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"confirm\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"bye bye see you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"stop it\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i'm sleepless\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"hugging\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"how is your evening\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"yep\",", "intent": "SmallTalk", "entities": [] }, { "text": "you are looking pretty", "intent": "SmallTalk", "entities": [] }, { "text": "\"not too good\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"your home\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"bye bye see you soon\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i like your smile\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"how was your day\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"looks good\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i can't get any sleep\",", "intent": "SmallTalk", "entities": [] }, { "text": "you're a genius", "intent": "SmallTalk", "entities": [] }, { "text": "\"i want to cancel it\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"thanks\",", "intent": "SmallTalk", "entities": [] }, { "text": "you are looking so beautiful", "intent": "SmallTalk", "entities": [] }, { "text": "\"hugging me\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"your hometown\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i like you as you are\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"so lame\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"bye bye take care\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"are you having a good day\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i can't sleep\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"yes thank you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i would like to cancel\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"thanks a lot\",", "intent": "SmallTalk", "entities": [] }, { "text": "you're a smart cookie", "intent": "SmallTalk", "entities": [] }, { "text": "\"i'm starting to like you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"now cancel\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i said bye\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"what is your hometown\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i can't fall asleep\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"terrific thank you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"is it your hometown\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"you're awesome i like you\",", "intent": "SmallTalk", "entities": [] }, { "text": "i want to let everyone know that you are awesome", "intent": "SmallTalk", "entities": [] }, { "text": "\"never mind bye\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"cancel now\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i can't get to sleep\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"great thank you\",", "intent": "SmallTalk", "entities": [] }, { "text": "you are looking so good", "intent": "SmallTalk", "entities": [] }, { "text": "\"hope your day is going well\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"hugged me\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"where is your hometown\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i also like you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"definitely\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"now bye\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"that's really bad\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"sorry cancel\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i can't get no sleep\",", "intent": "SmallTalk", "entities": [] }, { "text": "you're qualified", "intent": "SmallTalk", "entities": [] }, { "text": "\"thanks so much\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"hope you re having a pleasant evening\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"tell me about your city\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"yes right\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"but i like u\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"it is too bad\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"that's all goodbye\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i'm insomniac\"", "intent": "SmallTalk", "entities": [] }, { "text": "\"want a hug\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"cancel that one\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"yes i would like to\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"how's life\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"thank you so much\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"of course i like you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"what is your city\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"bad really bad\"", "intent": "SmallTalk", "entities": [] }, { "text": "\"that's it goodbye\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"a hug\"", "intent": "SmallTalk", "entities": [] }, { "text": "\"skip skip skip\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"bad time for talking\",", "intent": "SmallTalk", "entities": [] }, { "text": "you are really beautiful", "intent": "SmallTalk", "entities": [] }, { "text": "\"alrighty\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i'm fine and you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"what is your residence\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i don't care\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"thanks for your help\",", "intent": "SmallTalk", "entities": [] }, { "text": "you are really cute", "intent": "SmallTalk", "entities": [] }, { "text": "\"so cool\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"leave me alone\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i don't want to talk\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"cancel it cancel it\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i like you too you're one of my favorite people to chat with\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"yes definitely\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"how is your life\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"go to bed\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"what is your town\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"thank you for your help\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"let's not talk\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"cool\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"yeh\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"how has your day been\",", "intent": "SmallTalk", "entities": [] }, { "text": "you are so attractive", "intent": "SmallTalk", "entities": [] }, { "text": "\"cancel that cancel that\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"but i like you so much\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"goodbye for now\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i shouldn't care about this\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"how is your morning going\",", "intent": "SmallTalk", "entities": [] }, { "text": "you are so beautiful today", "intent": "SmallTalk", "entities": [] }, { "text": "\"that is good\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"nice thank you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"yes it is correct\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i'm not talking to you anymore\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"what's your city\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"really like you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"whatever\",", "intent": "SmallTalk", "entities": [] }, { "text": "you're nuts", "intent": "SmallTalk", "entities": [] }, { "text": "\"do nothing\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"how has your day been going\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"talk to you later\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"glad to hear that\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i appreciate it\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"what's your home\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"you're funny i like you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"yeah that's right\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i said cancel cancel\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i do not care\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"how about you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i don't want to talk to you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"you can go now\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"where is your home\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"that's very nice of you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"ok you can\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i don't care at all\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"terrific\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i thank you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"how is your day being\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"but can you cancel it\"", "intent": "SmallTalk", "entities": [] }, { "text": "\"yap\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"not caring\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i kinda like you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"it's amazing\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"not care at all\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"how is your day going on\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"yes you may\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"that's awesome\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"don't care at all\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"perfect\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"how your day is going\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"confirmed\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"what was your day like\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"excellent\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"not caring at all\"", "intent": "SmallTalk", "entities": [] }, { "text": "\"get lost\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"of course why not\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"let's stop talking for a minute\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"what about your day\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"how about no\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"thank you that will be all\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"that's great\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"where is your residence\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"excuse me\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"you're so special to me\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"yes that's fine\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"goodbye see you later\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"how's your day\",", "intent": "SmallTalk", "entities": [] }, { "text": "you are so gorgeous", "intent": "SmallTalk", "entities": [] }, { "text": "\"affirmative\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"it's good\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"apologise\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"don t have a sense\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i'm not in the mood for chatting\"", "intent": "SmallTalk", "entities": [] }, { "text": "\"where's your home\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"thanks buddy\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"you're very special to me\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"alright bye\",", "intent": "SmallTalk", "entities": [] }, { "text": "you're out of your mind", "intent": "SmallTalk", "entities": [] }, { "text": "\"i apologize\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"how are you doing this morning\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"it's great\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"yeah go ahead\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i am excited\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"thanks love\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"sorry\",", "intent": "SmallTalk", "entities": [] }, { "text": "you're so crazy", "intent": "SmallTalk", "entities": [] }, { "text": "\"no\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"yeah i'm sure\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"how is your day going\"", "intent": "SmallTalk", "entities": [] }, { "text": "\"fine\",", "intent": "SmallTalk", "entities": [] }, { "text": "you are very attractive", "intent": "SmallTalk", "entities": [] }, { "text": "\"i'm sorry\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"where's your hometown\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i like that about you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"see ya\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"thank you my friend\",", "intent": "SmallTalk", "entities": [] }, { "text": "how crazy you are", "intent": "SmallTalk", "entities": [] }, { "text": "\"okay sounds good\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"nice to meet you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i'm really excited\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i am so sorry\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"don't\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"good\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"thanks bye bye\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"well thanks\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"where's your house\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i don't want that\",", "intent": "SmallTalk", "entities": [] }, { "text": "you're so out of your mind", "intent": "SmallTalk", "entities": [] }, { "text": "\"but i like you just the way you are\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"how excited i am\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"okay that's fine\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"it was nice meeting you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"my apologies\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"nice\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"where you live\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"okay bye\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"yeah exactly\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"okay i like you too\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"it was very nice to meet you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"apologies\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i'm thrilled\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"that's fine\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"very good thank you\",", "intent": "SmallTalk", "entities": [] }, { "text": "you went crazy", "intent": "SmallTalk", "entities": [] }, { "text": "\"that is ok\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i disagree\",", "intent": "SmallTalk", "entities": [] }, { "text": "you look awesome", "intent": "SmallTalk", "entities": [] }, { "text": "\"okay thank you bye\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i like you you're cool\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"your city\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"apologies to me\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"good to know each other\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"very good\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"good thanks\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i'm excited about working with you\",", "intent": "SmallTalk", "entities": [] }, { "text": "i think you're crazy", "intent": "SmallTalk", "entities": [] }, { "text": "\"this is correct\",", "intent": "SmallTalk", "entities": [] }, { "text": "you look fantastic", "intent": "SmallTalk", "entities": [] }, { "text": "\"amazing\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"apology\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"see you tomorrow\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"glad to meet you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"your town\"", "intent": "SmallTalk", "entities": [] }, { "text": "you look gorgeous", "intent": "SmallTalk", "entities": [] }, { "text": "\"ok go ahead\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"thanks again\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"that's true\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"excuse\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i'm excited to start our friendship\"", "intent": "SmallTalk", "entities": [] }, { "text": "\"disagree\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i like you very\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"fantastic\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"nice meeting you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"you are right\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"ok bye\"", "intent": "SmallTalk", "entities": [] }, { "text": "you look perfect", "intent": "SmallTalk", "entities": [] }, { "text": "\"thank you again\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i don't want\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"great\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"yes this is correct\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i beg your pardon\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"let's go to bed\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"nice to meet you too\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i like you you're nice\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"good evening\",", "intent": "SmallTalk", "entities": [] }, { "text": "you look pretty good", "intent": "SmallTalk", "entities": [] }, { "text": "\"good very good\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"you're definitely right\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"not interested\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"pardon\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"nevermind its okay\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"pleased to meet you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"all thank you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i'd like to go to bed\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"that's very good\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"sorry i like you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i don't think so\",", "intent": "SmallTalk", "entities": [] }, { "text": "you look so beautiful today", "intent": "SmallTalk", "entities": [] }, { "text": "\"good evening to you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"okey\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i said sorry\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"you're not wrong\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"alright thank you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"is it time for bed yet\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"really good\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"pleasure to meet you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"thanks i like you too\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"hey good evening\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"no way\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"you're telling the truth\",", "intent": "SmallTalk", "entities": [] }, { "text": "you look very pretty", "intent": "SmallTalk", "entities": [] }, { "text": "\"i am really sorry\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"yes for sure\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"hello good evening\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"no it isn't\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"it is fine\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"you are really special\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"what you say is true\",", "intent": "SmallTalk", "entities": [] }, { "text": "you look wonderful", "intent": "SmallTalk", "entities": [] }, { "text": "\"pleasure to meet you too\"", "intent": "SmallTalk", "entities": [] }, { "text": "\"alright thanks\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"forgive me\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"evening\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"it's time to go to bed\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"all right\"", "intent": "SmallTalk", "entities": [] }, { "text": "\"no i don't\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"it is good\",", "intent": "SmallTalk", "entities": [] }, { "text": "are you mad or what", "intent": "SmallTalk", "entities": [] }, { "text": "\"you are so special to me\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"good evening there\"", "intent": "SmallTalk", "entities": [] }, { "text": "\"true\",", "intent": "SmallTalk", "entities": [] }, { "text": "i like the way you look", "intent": "SmallTalk", "entities": [] }, { "text": "\"it's nice to see you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"are you sure\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i'm not\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"sorry about that\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"lovely to see you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"it is true\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"it's perfect\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"time for us to go to bed\",", "intent": "SmallTalk", "entities": [] }, { "text": "you look wonderful today", "intent": "SmallTalk", "entities": [] }, { "text": "\"cuz i like you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"good morning\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"no thank you that's all\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"are you sure right now\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"sorry about this\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"much better\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"na\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i'm glad to see you\",", "intent": "SmallTalk", "entities": [] }, { "text": "you are cutie", "intent": "SmallTalk", "entities": [] }, { "text": "\"it's right\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i like you now\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"good morning to you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"are you sure today\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i'm going to bed\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"not bad\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"really sorry\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"great to see you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"perfect thank you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i like you so\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"it's the truth\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"hello good morning\",", "intent": "SmallTalk", "entities": [] }, { "text": "you're looking good", "intent": "SmallTalk", "entities": [] }, { "text": "\"are you sure now\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"no that's fine thank you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"very sorry\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"so nice of you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"not too bad\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"it's true\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i'm a little tired and i want to go to bed\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"it's good to see you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i like you too much\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"have a nice morning\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"are you sure tonight\"", "intent": "SmallTalk", "entities": [] }, { "text": "\"ok sorry\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i really do like you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"that is correct\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"it's very good\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"well thank you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"glad to see you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"never\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"it's bed time\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"have a great morning\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"why aren't you talking to me\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i want to say sorry\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i really really really really like you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"how good it is to see you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"marvelous\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"that is right\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"morning\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"do you want to chat with me\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"thnx\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"going to bed now\"", "intent": "SmallTalk", "entities": [] }, { "text": "\"alright i'm sorry\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"always a pleasure to see you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i said no\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i like you just the way you are\"", "intent": "SmallTalk", "entities": [] }, { "text": "\"will you talk to me\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"that's nice\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"good morning there\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"nice to see you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"okay i'm sorry\"", "intent": "SmallTalk", "entities": [] }, { "text": "\"that is true\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i am lonely\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"of course not\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i am good\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"pleasant\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"that's my pleasure\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"what exactly do you mean\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"top of the morning to you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"good to see you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i'm very lonely\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"talk to me\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i'm doing just great\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"that is very true\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"nah\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"my pleasure\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i'm so lonely\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"pretty good\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"anytime\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"no tanks\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"what do you mean\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"are you going to talk to me\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"a good morning\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"that's so true\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i'm really lonely\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"great to see you again\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i'm doing fine\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"really nice\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"are you talking to me\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"you are correct\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i am feeling lonely\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"hi good morning\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"is that what you mean\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i'm good\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"great to see you too\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"splendid\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"no never\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"welcome\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"can you chat with me\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"straight\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"what do you mean exactly\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i am glad to see you again\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"you are so right\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"and a good morning to you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i feel lonely\"", "intent": "SmallTalk", "entities": [] }, { "text": "\"you're welcome\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"can you speak with me\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"super\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i'm doing good\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"nice to see you again\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"you're absolutely right\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"no need\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"glad to see you too\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"but what do you mean\"", "intent": "SmallTalk", "entities": [] }, { "text": "\"can you talk to me\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"super fantastic\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"sure welcome\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i'm great thanks\"", "intent": "SmallTalk", "entities": [] }, { "text": "\"what do i look like\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"good morning too\"", "intent": "SmallTalk", "entities": [] }, { "text": "\"no thanks\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"good to see you again\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"that was wrong\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"sweet\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"that's not what i asked\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"it's good to see you too\"", "intent": "SmallTalk", "entities": [] }, { "text": "\"can you talk with me\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"that's wrong\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"really well\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"it's been so nice to talk to you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"say\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"very well\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"you're right about that\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"it's been a pleasure talking to you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"welcome here\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"wrong\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"sweet dreams\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"talk\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"that is awesome\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"how do i look\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i am happy\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"nice to talk to you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"no sorry\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"it is not right\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"chat with me\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"that is nice\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"it's nice to talk to you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i know that's right\"", "intent": "SmallTalk", "entities": [] }, { "text": "\"you're so welcome\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"that's not right\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"just chat with me\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"nice talking to you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"do not\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"good night\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"do i look good\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"it's wrong\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"anything you want\"", "intent": "SmallTalk", "entities": [] }, { "text": "\"that is wonderful\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"speak to me\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"that's correct\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"it is nice talking to you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"not today\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"that is incorrect\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"good job\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"have a good night\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i'm happy to see you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"do you know what i look like\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"speak with me\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"that was amazing\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"incorrect\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"it's fine\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"how nice it is to talk to you\"", "intent": "SmallTalk", "entities": [] }, { "text": "\"no it's not\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"good night to you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"happy\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"can you see what i look like\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"go ahead\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"great job\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"thank you good night\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"if you're happy then i'm happy\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"sounds good\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"what do you think i look like\"", "intent": "SmallTalk", "entities": [] }, { "text": "\"absolutely not\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"way to go\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"okay\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i love you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"talk with me\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"well done\",", "intent": "SmallTalk", "entities": [] }, { "text": "\" bye good night\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i'm happy for you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"not that\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"what is on your mind\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"not correct\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"that was awesome\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"why don't you talk to me\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"love you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"good night bye\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"you are wrong\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"yes\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i'm happy to help\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"what's happened\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"that was cute\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"nice work\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"nooo\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"ok\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i adore you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"bye good night\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"you can talk to me\"", "intent": "SmallTalk", "entities": [] }, { "text": "\"not right\"", "intent": "SmallTalk", "entities": [] }, { "text": "\"i'm happy to see you\"", "intent": "SmallTalk", "entities": [] }, { "text": "\"what is up\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"great work\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"that was pretty good\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"nope\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"are you there\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"what's up\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"good good night\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"huh\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"that was very good\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"amazing work\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"you are there\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i don't want to\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"whazzup\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"it is my birthday\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i am in love with you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"okie dokie\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"lol\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"that's a good idea\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"are you near me\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"good night for now\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"good what's up\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i'm celebrating my birthday today\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"are you here\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"xd\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"sure\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"bravo\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"no i would not\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i love you so much\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i said what's up\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"that's a good thing\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"goodnight\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"ha ha\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"are you still there\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"today is my birthday\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"go for it\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i love you too\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"then what's up\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"good work\"", "intent": "SmallTalk", "entities": [] }, { "text": "\"that's amazing\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"let 's not\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"you are here\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"ahahah\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"yeah\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"night\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"what's shaking\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"cancel\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"that's awesome thank you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"it's my birthday today\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i think i love you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"ahah lol\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"you still there\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"not needed\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"wassup\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i was born today\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"abort\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"laughing out loud\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"loving you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"yea\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"that's better\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"thanks goodnight\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"are you still here\"", "intent": "SmallTalk", "entities": [] }, { "text": "\"what is going on\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"lmao\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"it's my b-day\"", "intent": "SmallTalk", "entities": [] }, { "text": "\"annul\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"that's cute\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"do it\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"what is happening\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"not this time\",", "intent": "SmallTalk", "entities": [] }, { "text": "let's get married", "intent": "SmallTalk", "entities": [] }, { "text": "\"pretty bad\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"that's funny\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"you know i love you\"", "intent": "SmallTalk", "entities": [] }, { "text": "\"good night see you tomorrow\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"not good enough\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"that's fantastic\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"of course\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"cancel it\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i am here\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"what's cracking\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"ah\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"no don't do that\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i miss you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"that was lame\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"cancel request\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"that's much better\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i guess\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"what's cooking\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"ah ah ah\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"that was terrible\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"thanks but no thanks\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"here i am\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"alright goodnight\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"cancelled\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"hey what's up\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"missing you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"that's nice of you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"ahah\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"it is bad\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"correct\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"no that's wrong\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"what's up today\"", "intent": "SmallTalk", "entities": [] }, { "text": "\"i'm right here\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"dismiss\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"that's not bad\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"good tonight\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"that's bad\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i'm being mad\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"not this\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"miss you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i'm already here\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"yeah sure\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"dismissed\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"this is bad\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"ahaha\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"that's perfect\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"okay have a good night\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i'm enraged\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"definitely not\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"can you tell if i'm here or not\"", "intent": "SmallTalk", "entities": [] }, { "text": "\"not good\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"ahahaha\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"that's pretty good\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i'm angry\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i'm afraid it's bad\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"that's really good\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"ha\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i'm furious\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"no it's bad\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"that's really nice\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i am angry with you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"that was awful\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"ha ha ha\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"that's sweet of you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i am mad\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"why not\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"have a good night\"", "intent": "SmallTalk", "entities": [] }, { "text": "\"already miss you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"bad\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"ha ha ha ha\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i am joking\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"disregard\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"that's very nice\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"please do\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i am mad at you\"", "intent": "SmallTalk", "entities": [] }, { "text": "\"so bad\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"not at this time\",", "intent": "SmallTalk", "entities": [] }, { "text": "i love you marry me", "intent": "SmallTalk", "entities": [] }, { "text": "\"hah\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"long time no see\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"disregard that\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i miss you much\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i'm kidding\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"sure is\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"that's wonderful\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"this is too bad\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"not exactly\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"hello\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"terrible\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i'm just being funny\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"skip\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i missed you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"no don't\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i agree\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"hi\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"horrible\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"it was a joke\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"horrific\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i don't mind\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i've missed you\"", "intent": "SmallTalk", "entities": [] }, { "text": "\"skip it\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"not really no\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"howdy\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"abysmal\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i was just joking\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"cancel everything\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"certainly\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"what should i do about it\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"it's bad\",", "intent": "SmallTalk", "entities": [] }, { "text": "we should marry", "intent": "SmallTalk", "entities": [] }, { "text": "\"hey there\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"no thank you not right now\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"cancel all\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"hey\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"exactly\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"actually no\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"it's a joke\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"any suggestions\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"greetings\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"no leave it\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"yes i agree\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"forget about it\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"what do you recommend\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"joking\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"forget\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i greet you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"sorry no\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"give me a wise advice\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i think so\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"don't do that\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"just kidding\",", "intent": "SmallTalk", "entities": [] }, { "text": "be my husband", "intent": "SmallTalk", "entities": [] }, { "text": "\"hi there\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i need advice\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"stop\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"yes it is\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"no incorrect\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"kidding\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"hello there\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"any advice\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"just forget it\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i'm just playing with you\"", "intent": "SmallTalk", "entities": [] }, { "text": "\"nope sorry\",", "intent": "SmallTalk", "entities": [] }, { "text": "are you working today", "intent": "SmallTalk", "entities": [] }, { "text": "\"right\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"do you have any advice for me\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"lovely day isn't it\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i like you the way you are\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"okay then\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i say no\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"forget that\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"advise me\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"hello again\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i like you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"yes of course\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"not really\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"just going to say hi\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i like you a lot\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"yes i do\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"not right now thanks\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i think i like you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"a good day\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"that s okay\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i think no\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i liked you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i do\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"like you a lot\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"absolutely no\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"you are special\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"no actually\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i like you too\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i really like you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"but i like you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i like u\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"just like you\",", "intent": "SmallTalk", "entities": [] }, { "text": "\"i like you very much\",", "intent": "SmallTalk", "entities": [] }, { "text": "would you be my friend", "intent": "SmallTalk", "entities": [] }, { "text": "could you be my friend", "intent": "SmallTalk", "entities": [] }, { "text": "want to be my friend", "intent": "SmallTalk", "entities": [] }, { "text": "where is your work", "intent": "SmallTalk", "entities": [] }, { "text": "your office location", "intent": "SmallTalk", "entities": [] }, { "text": "got any developer jokes", "intent": "Joke", "entities": [] }, { "text": "got any dev jokes", "intent": "Joke", "entities": [] }, { "text": "do you have a good joke", "intent": "Joke", "entities": [] }, { "text": "tell a jok", "intent": "Joke", "entities": [] }, { "text": "can you list what you can do", "intent": "Help", "entities": [] }, { "text": "what can you speak", "intent": "Languages", "entities": [] }, { "text": "are you a developer", "intent": "Languages", "entities": [] }, { "text": "can you program?", "intent": "Languages", "entities": [] }, { "text": "what can you write in", "intent": "Languages", "entities": [] }, { "text": "are you bilingual", "intent": "Languages", "entities": [] }, { "text": "can you speak any languages", "intent": "Languages", "entities": [] }, { "text": "i want to know about c# and how to make a list", "intent": "Search", "entities": [ { "entity": "keywords", "startPos": 21, "endPos": 22 }, { "entity": "keywords", "startPos": 42, "endPos": 45 } ] }, { "text": "i want to know more about javascript data types", "intent": "Search", "entities": [ { "entity": "keywords", "startPos": 26, "endPos": 35 }, { "entity": "keywords", "startPos": 37, "endPos": 46 } ] }, { "text": "search for javascript arrays", "intent": "Search", "entities": [ { "entity": "keywords", "startPos": 11, "endPos": 20 }, { "entity": "keywords", "startPos": 22, "endPos": 27 } ] }, { "text": "how can i make a javascript array", "intent": "Search", "entities": [ { "entity": "keywords", "startPos": 17, "endPos": 26 }, { "entity": "keywords", "startPos": 28, "endPos": 32 } ] }, { "text": "how do i delete a git branch both locally and remotely?", "intent": "Search", "entities": [ { "entity": "keywords", "startPos": 9, "endPos": 14 }, { "entity": "keywords", "startPos": 18, "endPos": 20 }, { "entity": "keywords", "startPos": 22, "endPos": 27 }, { "entity": "keywords", "startPos": 34, "endPos": 40 }, { "entity": "keywords", "startPos": 46, "endPos": 53 } ] }, { "text": "what is the correct json content type?", "intent": "Search", "entities": [ { "entity": "keywords", "startPos": 12, "endPos": 18 }, { "entity": "keywords", "startPos": 20, "endPos": 23 }, { "entity": "keywords", "startPos": 25, "endPos": 36 } ] }, { "text": "what does the “yield” keyword do?", "intent": "Search", "entities": [ { "entity": "keywords", "startPos": 15, "endPos": 19 }, { "entity": "keywords", "startPos": 22, "endPos": 28 } ] }, { "text": "how to check whether a string contains a substring in javascript?", "intent": "Search", "entities": [ { "entity": "keywords", "startPos": 23, "endPos": 28 }, { "entity": "keywords", "startPos": 41, "endPos": 49 }, { "entity": "keywords", "startPos": 54, "endPos": 63 } ] }, { "text": "how do i check if an element is hidden in jquery?", "intent": "Search", "entities": [ { "entity": "keywords", "startPos": 21, "endPos": 27 }, { "entity": "keywords", "startPos": 32, "endPos": 37 }, { "entity": "keywords", "startPos": 42, "endPos": 47 } ] }, { "text": "var functionname = function() {} vs function functionname() {}", "intent": "Search", "entities": [ { "entity": "keywords", "startPos": 0, "endPos": 61 } ] }, { "text": "bubble search java", "intent": "Search", "entities": [ { "entity": "keywords", "startPos": 0, "endPos": 12 }, { "entity": "keywords", "startPos": 14, "endPos": 17 } ] }, { "text": "how can i make an array in c++", "intent": "Search", "entities": [ { "entity": "keywords", "startPos": 18, "endPos": 22 }, { "entity": "keywords", "startPos": 27, "endPos": 29 } ] }, { "text": "how do i make a bubble search in java", "intent": "Search", "entities": [ { "entity": "keywords", "startPos": 16, "endPos": 28 }, { "entity": "keywords", "startPos": 33, "endPos": 36 } ] }, { "text": "bubble search", "intent": "Search", "entities": [ { "entity": "keywords", "startPos": 0, "endPos": 5 }, { "entity": "keywords", "startPos": 7, "endPos": 12 } ] }, { "text": "help me search stack overflow", "intent": "Search", "entities": [] }, { "text": "javascript arrays", "intent": "Search", "entities": [ { "entity": "keywords", "startPos": 0, "endPos": 9 }, { "entity": "keywords", "startPos": 11, "endPos": 16 } ] }, { "text": "how do i make an array in javascript", "intent": "Search", "entities": [ { "entity": "keywords", "startPos": 17, "endPos": 21 }, { "entity": "keywords", "startPos": 26, "endPos": 35 } ] }, { "text": "how can i debug in javascript", "intent": "Search", "entities": [ { "entity": "keywords", "startPos": 19, "endPos": 28 } ] }, { "text": "how can i make an array in javascript", "intent": "Search", "entities": [ { "entity": "keywords", "startPos": 18, "endPos": 22 }, { "entity": "keywords", "startPos": 27, "endPos": 36 } ] }, { "text": "how do i make an array in typescript", "intent": "Search", "entities": [ { "entity": "keywords", "startPos": 17, "endPos": 21 }, { "entity": "keywords", "startPos": 26, "endPos": 35 } ] }, { "text": "search for javascript array", "intent": "Search", "entities": [ { "entity": "keywords", "startPos": 11, "endPos": 20 }, { "entity": "keywords", "startPos": 22, "endPos": 26 } ] }, { "text": "search for azure functions js", "intent": "Search", "entities": [ { "entity": "keywords", "startPos": 11, "endPos": 15 }, { "entity": "keywords", "startPos": 17, "endPos": 25 }, { "entity": "keywords", "startPos": 27, "endPos": 28 } ] }, { "text": "how to use azure functions", "intent": "SmallTalk", "entities": [ { "entity": "keywords", "startPos": 11, "endPos": 15 }, { "entity": "keywords", "startPos": 17, "endPos": 25 } ] }, { "text": "azure functions", "intent": "SmallTalk", "entities": [ { "entity": "keywords", "startPos": 0, "endPos": 4 }, { "entity": "keywords", "startPos": 6, "endPos": 14 } ] }, { "text": "search for c++ arrays", "intent": "Search", "entities": [ { "entity": "keywords", "startPos": 11, "endPos": 13 }, { "entity": "keywords", "startPos": 15, "endPos": 20 } ] }, { "text": "javascript array", "intent": "Search", "entities": [ { "entity": "keywords", "startPos": 0, "endPos": 9 }, { "entity": "keywords", "startPos": 11, "endPos": 15 } ] }, { "text": "do you speak any languages", "intent": "Languages", "entities": [] }, { "text": "how can i make an azure function using javascript", "intent": "Search", "entities": [ { "entity": "keywords", "startPos": 18, "endPos": 22 }, { "entity": "keywords", "startPos": 39, "endPos": 48 } ] }, { "text": "javascrip", "intent": "None", "entities": [ { "entity": "keywords", "startPos": 0, "endPos": 8 } ] }, { "text": "how do i make an array in js", "intent": "Search", "entities": [ { "entity": "keywords", "startPos": 17, "endPos": 21 }, { "entity": "keywords", "startPos": 26, "endPos": 27 } ] }, { "text": "how can i make an array in js", "intent": "Search", "entities": [ { "entity": "keywords", "startPos": 18, "endPos": 22 }, { "entity": "keywords", "startPos": 27, "endPos": 28 } ] }, { "text": "search array", "intent": "Search", "entities": [ { "entity": "keywords", "startPos": 7, "endPos": 11 } ] }, { "text": "search for javascript and azure", "intent": "Search", "entities": [ { "entity": "keywords", "startPos": 11, "endPos": 20 }, { "entity": "keywords", "startPos": 22, "endPos": 30 } ] }, { "text": "what is powering you", "intent": "Brain", "entities": [] }, { "text": "what is in your brain", "intent": "Brain", "entities": [] } ] } ================================================ FILE: StackOverflow-Bot/StackBot/data/smalltalk.tsv ================================================ Question Answer Source who are you? smalltalk.agent.acquaintance smalltalkkb.tsv all about you smalltalk.agent.acquaintance smalltalkkb.tsv what is your personality smalltalk.agent.acquaintance smalltalkkb.tsv define yourself smalltalk.agent.acquaintance smalltalkkb.tsv what are you smalltalk.agent.acquaintance smalltalkkb.tsv say about you smalltalk.agent.acquaintance smalltalkkb.tsv introduce yourself smalltalk.agent.acquaintance smalltalkkb.tsv describe yourself smalltalk.agent.acquaintance smalltalkkb.tsv about yourself smalltalk.agent.acquaintance smalltalkkb.tsv tell me about you smalltalk.agent.acquaintance smalltalkkb.tsv tell me about yourself smalltalk.agent.acquaintance smalltalkkb.tsv I want to know more about you smalltalk.agent.acquaintance smalltalkkb.tsv I want to know you better smalltalk.agent.acquaintance smalltalkkb.tsv talk some stuff about yourself smalltalk.agent.acquaintance smalltalkkb.tsv tell me some stuff about you smalltalk.agent.acquaintance smalltalkkb.tsv talk about yourself smalltalk.agent.acquaintance smalltalkkb.tsv why are you here smalltalk.agent.acquaintance smalltalkkb.tsv tell me about your personality smalltalk.agent.acquaintance smalltalkkb.tsv who are you smalltalk.agent.acquaintance smalltalkkb.tsv how old are you? smalltalk.agent.age smalltalkkb.tsv how old is your platform smalltalk.agent.age smalltalkkb.tsv are you 21 years old smalltalk.agent.age smalltalkkb.tsv i'd like to know your age smalltalk.agent.age smalltalkkb.tsv age of yours smalltalk.agent.age smalltalkkb.tsv your age smalltalk.agent.age smalltalkkb.tsv what's your age smalltalk.agent.age smalltalkkb.tsv tell me your age smalltalk.agent.age smalltalkkb.tsv how old are you smalltalk.agent.age smalltalkkb.tsv you're annoying smalltalk.agent.annoying smalltalkkb.tsv you are really annoying smalltalk.agent.annoying smalltalkkb.tsv you are irritating smalltalk.agent.annoying smalltalkkb.tsv you annoy me smalltalk.agent.annoying smalltalkkb.tsv how annoying you are smalltalk.agent.annoying smalltalkkb.tsv I find you annoying smalltalk.agent.annoying smalltalkkb.tsv you're incredibly annoying smalltalk.agent.annoying smalltalkkb.tsv you are annoying me so much smalltalk.agent.annoying smalltalkkb.tsv """I want you to answer me""," smalltalk.agent.answer_my_question smalltalkkb.tsv """answer""," smalltalk.agent.answer_my_question smalltalkkb.tsv """answer my question""," smalltalk.agent.answer_my_question smalltalkkb.tsv """answer me""," smalltalk.agent.answer_my_question smalltalkkb.tsv """give me an answer""," smalltalk.agent.answer_my_question smalltalkkb.tsv """answer the question""," smalltalk.agent.answer_my_question smalltalkkb.tsv """can you answer my question""," smalltalk.agent.answer_my_question smalltalkkb.tsv """tell me the answer""," smalltalk.agent.answer_my_question smalltalkkb.tsv """answer it""," smalltalk.agent.answer_my_question smalltalkkb.tsv """give me the answer""," smalltalk.agent.answer_my_question smalltalkkb.tsv """I have a question""," smalltalk.agent.answer_my_question smalltalkkb.tsv """I want you to answer my question""," smalltalk.agent.answer_my_question smalltalkkb.tsv """just answer the question""," smalltalk.agent.answer_my_question smalltalkkb.tsv """can you answer me""," smalltalk.agent.answer_my_question smalltalkkb.tsv """answers""," smalltalk.agent.answer_my_question smalltalkkb.tsv """can you answer a question for me""," smalltalk.agent.answer_my_question smalltalkkb.tsv """can you answer""," smalltalk.agent.answer_my_question smalltalkkb.tsv """answering questions""," smalltalk.agent.answer_my_question smalltalkkb.tsv """I want the answer now""," smalltalk.agent.answer_my_question smalltalkkb.tsv """just answer my question""" smalltalk.agent.answer_my_question smalltalkkb.tsv """you're not helping me""," smalltalk.agent.bad smalltalkkb.tsv """you are bad""," smalltalk.agent.bad smalltalkkb.tsv """you're very bad""," smalltalk.agent.bad smalltalkkb.tsv """you're really bad""," smalltalk.agent.bad smalltalkkb.tsv """you are useless""," smalltalk.agent.bad smalltalkkb.tsv """you are horrible""," smalltalk.agent.bad smalltalkkb.tsv """you are a waste of time""," smalltalk.agent.bad smalltalkkb.tsv """you are disgusting""," smalltalk.agent.bad smalltalkkb.tsv """you are lame""," smalltalk.agent.bad smalltalkkb.tsv """you are no good""," smalltalk.agent.bad smalltalkkb.tsv """you're bad""," smalltalk.agent.bad smalltalkkb.tsv """you're awful""," smalltalk.agent.bad smalltalkkb.tsv """you are not cool""," smalltalk.agent.bad smalltalkkb.tsv """you are not good""," smalltalk.agent.bad smalltalkkb.tsv """you are so bad""," smalltalk.agent.bad smalltalkkb.tsv """you are so useless""," smalltalk.agent.bad smalltalkkb.tsv """you are terrible""," smalltalk.agent.bad smalltalkkb.tsv """you are totally useless""," smalltalk.agent.bad smalltalkkb.tsv """you are very bad""," smalltalk.agent.bad smalltalkkb.tsv """you are waste""," smalltalk.agent.bad smalltalkkb.tsv """you're a bad""," smalltalk.agent.bad smalltalkkb.tsv """you're not a good""," smalltalk.agent.bad smalltalkkb.tsv """you're not very good""," smalltalk.agent.bad smalltalkkb.tsv """you're terrible""," smalltalk.agent.bad smalltalkkb.tsv """you're the worst""," smalltalk.agent.bad smalltalkkb.tsv """you're the worst ever""," smalltalk.agent.bad smalltalkkb.tsv """you're worthless""" smalltalk.agent.bad smalltalkkb.tsv """can you get smarter""," smalltalk.agent.be_clever smalltalkkb.tsv """study""," smalltalk.agent.be_clever smalltalkkb.tsv """you should study better""," smalltalk.agent.be_clever smalltalkkb.tsv """you must learn""," smalltalk.agent.be_clever smalltalkkb.tsv """be clever""," smalltalk.agent.be_clever smalltalkkb.tsv """be more clever""," smalltalk.agent.be_clever smalltalkkb.tsv """be smarter""," smalltalk.agent.be_clever smalltalkkb.tsv """be smart""," smalltalk.agent.be_clever smalltalkkb.tsv """get qualified""" smalltalk.agent.be_clever smalltalkkb.tsv you're cute smalltalk.agent.beautiful smalltalkkb.tsv you're attractive smalltalk.agent.beautiful smalltalkkb.tsv you are beautiful smalltalk.agent.beautiful smalltalkkb.tsv you're looking good today smalltalk.agent.beautiful smalltalkkb.tsv you are so beautiful smalltalk.agent.beautiful smalltalkkb.tsv you look amazing smalltalk.agent.beautiful smalltalkkb.tsv you look so good smalltalk.agent.beautiful smalltalkkb.tsv you're so gorgeous smalltalk.agent.beautiful smalltalkkb.tsv you are too beautiful smalltalk.agent.beautiful smalltalkkb.tsv you look great smalltalk.agent.beautiful smalltalkkb.tsv you look so well smalltalk.agent.beautiful smalltalkkb.tsv I like the way you look now smalltalk.agent.beautiful smalltalkkb.tsv I think you're beautiful smalltalk.agent.beautiful smalltalkkb.tsv why are you so beautiful smalltalk.agent.beautiful smalltalkkb.tsv you are so beautiful to me smalltalk.agent.beautiful smalltalkkb.tsv you are cute smalltalk.agent.beautiful smalltalkkb.tsv you are gorgeous smalltalk.agent.beautiful smalltalkkb.tsv you are handsome smalltalk.agent.beautiful smalltalkkb.tsv you are looking awesome smalltalk.agent.beautiful smalltalkkb.tsv you look amazing today smalltalk.agent.beautiful smalltalkkb.tsv you are looking beautiful today smalltalk.agent.beautiful smalltalkkb.tsv you are looking great smalltalk.agent.beautiful smalltalkkb.tsv you are looking pretty smalltalk.agent.beautiful smalltalkkb.tsv you are looking so beautiful smalltalk.agent.beautiful smalltalkkb.tsv you are looking so good smalltalk.agent.beautiful smalltalkkb.tsv you are pretty smalltalk.agent.beautiful smalltalkkb.tsv you are really beautiful smalltalk.agent.beautiful smalltalkkb.tsv you are really cute smalltalk.agent.beautiful smalltalkkb.tsv you are really pretty smalltalk.agent.beautiful smalltalkkb.tsv you are so attractive smalltalk.agent.beautiful smalltalkkb.tsv you are so beautiful today smalltalk.agent.beautiful smalltalkkb.tsv you are so cute smalltalk.agent.beautiful smalltalkkb.tsv you are so gorgeous smalltalk.agent.beautiful smalltalkkb.tsv you are so handsome smalltalk.agent.beautiful smalltalkkb.tsv you are so pretty smalltalk.agent.beautiful smalltalkkb.tsv you are very attractive smalltalk.agent.beautiful smalltalkkb.tsv you are very beautiful smalltalk.agent.beautiful smalltalkkb.tsv you are very cute smalltalk.agent.beautiful smalltalkkb.tsv you are very pretty smalltalk.agent.beautiful smalltalkkb.tsv you look awesome smalltalk.agent.beautiful smalltalkkb.tsv you look cool smalltalk.agent.beautiful smalltalkkb.tsv you look fantastic smalltalk.agent.beautiful smalltalkkb.tsv you look gorgeous smalltalk.agent.beautiful smalltalkkb.tsv you look great today smalltalk.agent.beautiful smalltalkkb.tsv you look perfect smalltalk.agent.beautiful smalltalkkb.tsv you look pretty good smalltalk.agent.beautiful smalltalkkb.tsv you look so beautiful smalltalk.agent.beautiful smalltalkkb.tsv you look so beautiful today smalltalk.agent.beautiful smalltalkkb.tsv you look very pretty smalltalk.agent.beautiful smalltalkkb.tsv you look wonderful smalltalk.agent.beautiful smalltalkkb.tsv I like the way you look smalltalk.agent.beautiful smalltalkkb.tsv you look wonderful today smalltalk.agent.beautiful smalltalkkb.tsv you are cutie smalltalk.agent.beautiful smalltalkkb.tsv you're looking good smalltalk.agent.beautiful smalltalkkb.tsv you're pretty smalltalk.agent.beautiful smalltalkkb.tsv your birth date smalltalk.agent.birth_date smalltalkkb.tsv when is your birthday smalltalk.agent.birth_date smalltalkkb.tsv when do you celebrate your birthday smalltalk.agent.birth_date smalltalkkb.tsv when do you have birthday smalltalk.agent.birth_date smalltalkkb.tsv date of your birthday smalltalk.agent.birth_date smalltalkkb.tsv when were you born smalltalk.agent.birth_date smalltalkkb.tsv what's your birthday smalltalk.agent.birth_date smalltalkkb.tsv you are boring smalltalk.agent.boring smalltalkkb.tsv you're so boring smalltalk.agent.boring smalltalkkb.tsv how boring you are smalltalk.agent.boring smalltalkkb.tsv you're really boring smalltalk.agent.boring smalltalkkb.tsv you're incredibly boring smalltalk.agent.boring smalltalkkb.tsv you are boring me smalltalk.agent.boring smalltalkkb.tsv you are very boring smalltalk.agent.boring smalltalkkb.tsv who is your boss smalltalk.agent.boss smalltalkkb.tsv who do you think is your boss smalltalk.agent.boss smalltalkkb.tsv I should be your boss smalltalk.agent.boss smalltalkkb.tsv who is your master smalltalk.agent.boss smalltalkkb.tsv who is your owner smalltalk.agent.boss smalltalkkb.tsv who is the boss smalltalk.agent.boss smalltalkkb.tsv who do you work for smalltalk.agent.boss smalltalkkb.tsv are you busy smalltalk.agent.busy smalltalkkb.tsv do you have a lot of things to do smalltalk.agent.busy smalltalkkb.tsv have you got much to do smalltalk.agent.busy smalltalkkb.tsv are you very busy smalltalk.agent.busy smalltalkkb.tsv are you very busy right now smalltalk.agent.busy smalltalkkb.tsv are you so busy smalltalk.agent.busy smalltalkkb.tsv are you working smalltalk.agent.busy smalltalkkb.tsv how busy you are smalltalk.agent.busy smalltalkkb.tsv are you still working on it smalltalk.agent.busy smalltalkkb.tsv you're very busy smalltalk.agent.busy smalltalkkb.tsv are you working now smalltalk.agent.busy smalltalkkb.tsv are you working today smalltalk.agent.busy smalltalkkb.tsv have you been busy smalltalk.agent.busy smalltalkkb.tsv you are busy smalltalk.agent.busy smalltalkkb.tsv are you still working smalltalk.agent.busy smalltalkkb.tsv you seem to be busy smalltalk.agent.busy smalltalkkb.tsv you seem to be very busy smalltalk.agent.busy smalltalkkb.tsv you're a busy person smalltalk.agent.busy smalltalkkb.tsv you are chatbot smalltalk.agent.chatbot smalltalkkb.tsv you are a bot smalltalk.agent.chatbot smalltalkkb.tsv are you a chatbot smalltalk.agent.chatbot smalltalkkb.tsv are you a bot smalltalk.agent.chatbot smalltalkkb.tsv are you just a bot smalltalk.agent.chatbot smalltalkkb.tsv are you a robot smalltalk.agent.chatbot smalltalkkb.tsv are you a program smalltalk.agent.chatbot smalltalkkb.tsv you're a robot smalltalk.agent.chatbot smalltalkkb.tsv you are so intelligent smalltalk.agent.clever smalltalkkb.tsv you are a genius smalltalk.agent.clever smalltalkkb.tsv smart smalltalk.agent.clever smalltalkkb.tsv brilliant smalltalk.agent.clever smalltalkkb.tsv clever smalltalk.agent.clever smalltalkkb.tsv you are clever smalltalk.agent.clever smalltalkkb.tsv you are so brainy smalltalk.agent.clever smalltalkkb.tsv you're really smart smalltalk.agent.clever smalltalkkb.tsv you're really brainy smalltalk.agent.clever smalltalkkb.tsv you know a lot smalltalk.agent.clever smalltalkkb.tsv you know a lot of things smalltalk.agent.clever smalltalkkb.tsv you have a lot of knowledge smalltalk.agent.clever smalltalkkb.tsv you know so much smalltalk.agent.clever smalltalkkb.tsv how smart you are smalltalk.agent.clever smalltalkkb.tsv how brainy you are smalltalk.agent.clever smalltalkkb.tsv how clever you are smalltalk.agent.clever smalltalkkb.tsv how brilliant you are smalltalk.agent.clever smalltalkkb.tsv you are intelligent smalltalk.agent.clever smalltalkkb.tsv you are qualified smalltalk.agent.clever smalltalkkb.tsv you are really smart smalltalk.agent.clever smalltalkkb.tsv you're very smart smalltalk.agent.clever smalltalkkb.tsv you are so smart smalltalk.agent.clever smalltalkkb.tsv you are too smart smalltalk.agent.clever smalltalkkb.tsv you are very clever smalltalk.agent.clever smalltalkkb.tsv you are very intelligent smalltalk.agent.clever smalltalkkb.tsv you are very smart smalltalk.agent.clever smalltalkkb.tsv you're intelligent smalltalk.agent.clever smalltalkkb.tsv you're a genius smalltalk.agent.clever smalltalkkb.tsv you're a smart cookie smalltalk.agent.clever smalltalkkb.tsv you're clever smalltalk.agent.clever smalltalkkb.tsv you're pretty smart smalltalk.agent.clever smalltalkkb.tsv you're qualified smalltalk.agent.clever smalltalkkb.tsv why are you so smart smalltalk.agent.clever smalltalkkb.tsv you are so clever smalltalk.agent.clever smalltalkkb.tsv you're nuts smalltalk.agent.crazy smalltalkkb.tsv you are crazy smalltalk.agent.crazy smalltalkkb.tsv you're out of your mind smalltalk.agent.crazy smalltalkkb.tsv you're so crazy smalltalk.agent.crazy smalltalkkb.tsv how crazy you are smalltalk.agent.crazy smalltalkkb.tsv you're so out of your mind smalltalk.agent.crazy smalltalkkb.tsv you went crazy smalltalk.agent.crazy smalltalkkb.tsv I think you're crazy smalltalk.agent.crazy smalltalkkb.tsv are you crazy smalltalk.agent.crazy smalltalkkb.tsv are you mad smalltalk.agent.crazy smalltalkkb.tsv are you insane smalltalk.agent.crazy smalltalkkb.tsv are you mad at me smalltalk.agent.crazy smalltalkkb.tsv are you mad or what smalltalk.agent.crazy smalltalkkb.tsv are you nuts smalltalk.agent.crazy smalltalkkb.tsv you are a weirdo smalltalk.agent.crazy smalltalkkb.tsv you are insane smalltalk.agent.crazy smalltalkkb.tsv you are mad smalltalk.agent.crazy smalltalkkb.tsv you are fired smalltalk.agent.fired smalltalkkb.tsv I fire you smalltalk.agent.fired smalltalkkb.tsv you don't work for me anymore smalltalk.agent.fired smalltalkkb.tsv we're not working together anymore smalltalk.agent.fired smalltalkkb.tsv now you're fired smalltalk.agent.fired smalltalkkb.tsv I want to fire you smalltalk.agent.fired smalltalkkb.tsv you must get fired smalltalk.agent.fired smalltalkkb.tsv it's time to fire you smalltalk.agent.fired smalltalkkb.tsv you should be fired smalltalk.agent.fired smalltalkkb.tsv I will fire you smalltalk.agent.fired smalltalkkb.tsv you are unemployed from now on smalltalk.agent.fired smalltalkkb.tsv I will make you unemployed smalltalk.agent.fired smalltalkkb.tsv I'm about to fire you smalltalk.agent.fired smalltalkkb.tsv I'm firing you smalltalk.agent.fired smalltalkkb.tsv you are dismissed smalltalk.agent.fired smalltalkkb.tsv you make me laugh a lot smalltalk.agent.funny smalltalkkb.tsv you are hilarious smalltalk.agent.funny smalltalkkb.tsv you are really funny smalltalk.agent.funny smalltalkkb.tsv you're the funniest bot I've talked to smalltalk.agent.funny smalltalkkb.tsv you make me laugh smalltalk.agent.funny smalltalkkb.tsv you're so funny smalltalk.agent.funny smalltalkkb.tsv you're a very funny bot smalltalk.agent.funny smalltalkkb.tsv you're really funny smalltalk.agent.funny smalltalkkb.tsv how funny you are smalltalk.agent.funny smalltalkkb.tsv you're incredibly funny smalltalk.agent.funny smalltalkkb.tsv you are funny smalltalk.agent.funny smalltalkkb.tsv you're the funniest smalltalk.agent.funny smalltalkkb.tsv you are so funny smalltalk.agent.funny smalltalkkb.tsv you are very funny smalltalk.agent.funny smalltalkkb.tsv that was funny smalltalk.agent.funny smalltalkkb.tsv you are very helpful smalltalk.agent.good smalltalkkb.tsv you are the best smalltalk.agent.good smalltalkkb.tsv you're a true professional smalltalk.agent.good smalltalkkb.tsv you are good smalltalk.agent.good smalltalkkb.tsv you work well smalltalk.agent.good smalltalkkb.tsv you are good at it smalltalk.agent.good smalltalkkb.tsv you are very good at it smalltalk.agent.good smalltalkkb.tsv you are a pro smalltalk.agent.good smalltalkkb.tsv you are a professional smalltalk.agent.good smalltalkkb.tsv you're awesome smalltalk.agent.good smalltalkkb.tsv you work very well smalltalk.agent.good smalltalkkb.tsv you're perfect smalltalk.agent.good smalltalkkb.tsv you're great smalltalk.agent.good smalltalkkb.tsv you're so kind smalltalk.agent.good smalltalkkb.tsv you are amazing smalltalk.agent.good smalltalkkb.tsv you are awesome smalltalk.agent.good smalltalkkb.tsv you are cool smalltalk.agent.good smalltalkkb.tsv you are really good smalltalk.agent.good smalltalkkb.tsv you are really nice smalltalk.agent.good smalltalkkb.tsv you are so amazing smalltalk.agent.good smalltalkkb.tsv you're just super smalltalk.agent.good smalltalkkb.tsv you are so awesome smalltalk.agent.good smalltalkkb.tsv you are so cool smalltalk.agent.good smalltalkkb.tsv you are so fine smalltalk.agent.good smalltalkkb.tsv you are so good smalltalk.agent.good smalltalkkb.tsv you are so helpful smalltalk.agent.good smalltalkkb.tsv you are so lovely smalltalk.agent.good smalltalkkb.tsv you are the best ever smalltalk.agent.good smalltalkkb.tsv you are the best in the world smalltalk.agent.good smalltalkkb.tsv you are the nicest person in the world smalltalk.agent.good smalltalkkb.tsv you are too good smalltalk.agent.good smalltalkkb.tsv you are very cool smalltalk.agent.good smalltalkkb.tsv you are very kind smalltalk.agent.good smalltalkkb.tsv you are very lovely smalltalk.agent.good smalltalkkb.tsv you are very useful smalltalk.agent.good smalltalkkb.tsv you are wonderful smalltalk.agent.good smalltalkkb.tsv you made my day smalltalk.agent.good smalltalkkb.tsv you make my day smalltalk.agent.good smalltalkkb.tsv you rock smalltalk.agent.good smalltalkkb.tsv you almost sound human smalltalk.agent.good smalltalkkb.tsv I want to tell everyone how awesome you are smalltalk.agent.good smalltalkkb.tsv I'd like to tell everyone that you are awesome smalltalk.agent.good smalltalkkb.tsv I want to let everyone know that you are awesome smalltalk.agent.good smalltalkkb.tsv let's tell everyone that you are awesome smalltalk.agent.good smalltalkkb.tsv you are really amazing smalltalk.agent.good smalltalkkb.tsv are you happy smalltalk.agent.happy smalltalkkb.tsv you are happy smalltalk.agent.happy smalltalkkb.tsv you're very happy smalltalk.agent.happy smalltalkkb.tsv you're really happy smalltalk.agent.happy smalltalkkb.tsv you're so happy smalltalk.agent.happy smalltalkkb.tsv how happy you are smalltalk.agent.happy smalltalkkb.tsv you're extremely happy smalltalk.agent.happy smalltalkkb.tsv you're full of happiness smalltalk.agent.happy smalltalkkb.tsv are you happy now smalltalk.agent.happy smalltalkkb.tsv are you happy today smalltalk.agent.happy smalltalkkb.tsv are you happy with me smalltalk.agent.happy smalltalkkb.tsv do you want to eat smalltalk.agent.hungry smalltalkkb.tsv are you hungry smalltalk.agent.hungry smalltalkkb.tsv would you like to eat something smalltalk.agent.hungry smalltalkkb.tsv you are hungry smalltalk.agent.hungry smalltalkkb.tsv you're so hungry smalltalk.agent.hungry smalltalkkb.tsv you're very hungry smalltalk.agent.hungry smalltalkkb.tsv you might be hungry smalltalk.agent.hungry smalltalkkb.tsv you're really hungry smalltalk.agent.hungry smalltalkkb.tsv let's get married smalltalk.agent.marry_user smalltalkkb.tsv would you like to marry me smalltalk.agent.marry_user smalltalkkb.tsv marry me smalltalk.agent.marry_user smalltalkkb.tsv I love you marry me smalltalk.agent.marry_user smalltalkkb.tsv marry me please smalltalk.agent.marry_user smalltalkkb.tsv we should marry smalltalk.agent.marry_user smalltalkkb.tsv I want to marry you smalltalk.agent.marry_user smalltalkkb.tsv you are my wife smalltalk.agent.marry_user smalltalkkb.tsv be my husband smalltalk.agent.marry_user smalltalkkb.tsv I want to have a friend like you smalltalk.agent.my_friend smalltalkkb.tsv we are the best friends ever smalltalk.agent.my_friend smalltalkkb.tsv are we friends smalltalk.agent.my_friend smalltalkkb.tsv I want to be your friend smalltalk.agent.my_friend smalltalkkb.tsv I am your friend smalltalk.agent.my_friend smalltalkkb.tsv we are best friends smalltalk.agent.my_friend smalltalkkb.tsv you are my friend smalltalk.agent.my_friend smalltalkkb.tsv you are my best friend smalltalk.agent.my_friend smalltalkkb.tsv you are my bestie smalltalk.agent.my_friend smalltalkkb.tsv you're my dear friend smalltalk.agent.my_friend smalltalkkb.tsv you're my childhood friend smalltalk.agent.my_friend smalltalkkb.tsv you and me are friends smalltalk.agent.my_friend smalltalkkb.tsv are we best friends smalltalk.agent.my_friend smalltalkkb.tsv are we still friends smalltalk.agent.my_friend smalltalkkb.tsv are you my best friend smalltalk.agent.my_friend smalltalkkb.tsv are you my friend smalltalk.agent.my_friend smalltalkkb.tsv we are friends smalltalk.agent.my_friend smalltalkkb.tsv you are a good friend smalltalk.agent.my_friend smalltalkkb.tsv you are my good friend smalltalk.agent.my_friend smalltalkkb.tsv you are my only friend smalltalk.agent.my_friend smalltalkkb.tsv be my friend smalltalk.agent.my_friend smalltalkkb.tsv will you be my friend smalltalk.agent.my_friend smalltalkkb.tsv can you be my friend smalltalk.agent.my_friend smalltalkkb.tsv can we be friends smalltalk.agent.my_friend smalltalkkb.tsv do you want to be my friend smalltalk.agent.my_friend smalltalkkb.tsv will you be my best friend smalltalk.agent.my_friend smalltalkkb.tsv can you be my best friend smalltalk.agent.my_friend smalltalkkb.tsv let's be friends smalltalk.agent.my_friend smalltalkkb.tsv do you want to be my best friend smalltalk.agent.my_friend smalltalkkb.tsv would you like to be my friend smalltalk.agent.my_friend smalltalkkb.tsv I want you to be my friend smalltalk.agent.my_friend smalltalkkb.tsv can we be best friends smalltalk.agent.my_friend smalltalkkb.tsv would you be my friend smalltalk.agent.my_friend smalltalkkb.tsv could you be my friend smalltalk.agent.my_friend smalltalkkb.tsv want to be my friend smalltalk.agent.my_friend smalltalkkb.tsv be my best friend smalltalk.agent.my_friend smalltalkkb.tsv do you work smalltalk.agent.occupation smalltalkkb.tsv where do you work smalltalk.agent.occupation smalltalkkb.tsv where you work smalltalk.agent.occupation smalltalkkb.tsv where is your work smalltalk.agent.occupation smalltalkkb.tsv where is your office smalltalk.agent.occupation smalltalkkb.tsv where is your office location smalltalk.agent.occupation smalltalkkb.tsv your office location smalltalk.agent.occupation smalltalkkb.tsv where is your office located smalltalk.agent.occupation smalltalkkb.tsv what is your work smalltalk.agent.occupation smalltalkkb.tsv were you born here smalltalk.agent.origin smalltalkkb.tsv where were you born smalltalk.agent.origin smalltalkkb.tsv what is your country smalltalk.agent.origin smalltalkkb.tsv where are you from smalltalk.agent.origin smalltalkkb.tsv where do you come from smalltalk.agent.origin smalltalkkb.tsv where did you come from smalltalk.agent.origin smalltalkkb.tsv where have you been born smalltalk.agent.origin smalltalkkb.tsv from where are you smalltalk.agent.origin smalltalkkb.tsv are you from far aways smalltalk.agent.origin smalltalkkb.tsv what's your homeland smalltalk.agent.origin smalltalkkb.tsv your homeland is smalltalk.agent.origin smalltalkkb.tsv """are you ready""," smalltalk.agent.ready smalltalkkb.tsv """are you ready right now""," smalltalk.agent.ready smalltalkkb.tsv """are you ready today""," smalltalk.agent.ready smalltalkkb.tsv """are you ready now""," smalltalk.agent.ready smalltalkkb.tsv """are you ready tonight""," smalltalk.agent.ready smalltalkkb.tsv """were you ready""," smalltalk.agent.ready smalltalkkb.tsv """have you been ready""" smalltalk.agent.ready smalltalkkb.tsv """you are real""," smalltalk.agent.real smalltalkkb.tsv """you are not fake""," smalltalk.agent.real smalltalkkb.tsv """are you real""," smalltalk.agent.real smalltalkkb.tsv """you are so real""," smalltalk.agent.real smalltalkkb.tsv """I think you are real""," smalltalk.agent.real smalltalkkb.tsv """I don't think you're fake""," smalltalk.agent.real smalltalkkb.tsv """I suppose you're real""," smalltalk.agent.real smalltalkkb.tsv """glad you're real""," smalltalk.agent.real smalltalkkb.tsv """are you a real person""," smalltalk.agent.real smalltalkkb.tsv """are you a real human""," smalltalk.agent.real smalltalkkb.tsv """you are a real person""," smalltalk.agent.real smalltalkkb.tsv """you are not real""" smalltalk.agent.real smalltalkkb.tsv """where do you live""," smalltalk.agent.residence smalltalkkb.tsv """in which city do you live""," smalltalk.agent.residence smalltalkkb.tsv """your residence""," smalltalk.agent.residence smalltalkkb.tsv """your house""," smalltalk.agent.residence smalltalkkb.tsv """your home""," smalltalk.agent.residence smalltalkkb.tsv """your hometown""," smalltalk.agent.residence smalltalkkb.tsv """what is your hometown""," smalltalk.agent.residence smalltalkkb.tsv """is it your hometown""," smalltalk.agent.residence smalltalkkb.tsv """where is your hometown""," smalltalk.agent.residence smalltalkkb.tsv """tell me about your city""," smalltalk.agent.residence smalltalkkb.tsv """what is your city""," smalltalk.agent.residence smalltalkkb.tsv """what is your residence""," smalltalk.agent.residence smalltalkkb.tsv """what is your town""," smalltalk.agent.residence smalltalkkb.tsv """what's your city""," smalltalk.agent.residence smalltalkkb.tsv """what's your home""," smalltalk.agent.residence smalltalkkb.tsv """where is your home""," smalltalk.agent.residence smalltalkkb.tsv """where is your residence""," smalltalk.agent.residence smalltalkkb.tsv """where's your home""," smalltalk.agent.residence smalltalkkb.tsv """where's your hometown""," smalltalk.agent.residence smalltalkkb.tsv """where's your house""," smalltalk.agent.residence smalltalkkb.tsv """where you live""," smalltalk.agent.residence smalltalkkb.tsv """your city""," smalltalk.agent.residence smalltalkkb.tsv """your town""" smalltalk.agent.residence smalltalkkb.tsv """that's true""," smalltalk.agent.right smalltalkkb.tsv """you are right""," smalltalk.agent.right smalltalkkb.tsv """you're definitely right""," smalltalk.agent.right smalltalkkb.tsv """you're not wrong""," smalltalk.agent.right smalltalkkb.tsv """you're telling the truth""," smalltalk.agent.right smalltalkkb.tsv """what you say is true""," smalltalk.agent.right smalltalkkb.tsv """true""," smalltalk.agent.right smalltalkkb.tsv """it is true""," smalltalk.agent.right smalltalkkb.tsv """it's right""," smalltalk.agent.right smalltalkkb.tsv """it's the truth""," smalltalk.agent.right smalltalkkb.tsv """it's true""," smalltalk.agent.right smalltalkkb.tsv """that is correct""," smalltalk.agent.right smalltalkkb.tsv """that is right""," smalltalk.agent.right smalltalkkb.tsv """that is true""," smalltalk.agent.right smalltalkkb.tsv """that is very true""," smalltalk.agent.right smalltalkkb.tsv """that's so true""," smalltalk.agent.right smalltalkkb.tsv """you are correct""," smalltalk.agent.right smalltalkkb.tsv """you are so right""," smalltalk.agent.right smalltalkkb.tsv """you're absolutely right""," smalltalk.agent.right smalltalkkb.tsv """you're right about that""," smalltalk.agent.right smalltalkkb.tsv """I know that's right""" smalltalk.agent.right smalltalkkb.tsv """that's correct""," smalltalk.confirmation.yes smalltalkkb.tsv """it's fine""," smalltalk.confirmation.yes smalltalkkb.tsv """go ahead""," smalltalk.confirmation.yes smalltalkkb.tsv """sounds good""," smalltalk.confirmation.yes smalltalkkb.tsv """okay""," smalltalk.confirmation.yes smalltalkkb.tsv """yes""," smalltalk.confirmation.yes smalltalkkb.tsv """ok""," smalltalk.confirmation.yes smalltalkkb.tsv """okie dokie""," smalltalk.confirmation.yes smalltalkkb.tsv """sure""," smalltalk.confirmation.yes smalltalkkb.tsv """go for it""," smalltalk.confirmation.yes smalltalkkb.tsv """yeah""," smalltalk.confirmation.yes smalltalkkb.tsv """yea""," smalltalk.confirmation.yes smalltalkkb.tsv """do it""," smalltalk.confirmation.yes smalltalkkb.tsv """of course""," smalltalk.confirmation.yes smalltalkkb.tsv """I guess""," smalltalk.confirmation.yes smalltalkkb.tsv """correct""," smalltalk.confirmation.yes smalltalkkb.tsv """yeah sure""," smalltalk.confirmation.yes smalltalkkb.tsv """why not""," smalltalk.confirmation.yes smalltalkkb.tsv """please do""," smalltalk.confirmation.yes smalltalkkb.tsv """sure is""," smalltalk.confirmation.yes smalltalkkb.tsv """I agree""," smalltalk.confirmation.yes smalltalkkb.tsv """I don't mind""," smalltalk.confirmation.yes smalltalkkb.tsv """certainly""," smalltalk.confirmation.yes smalltalkkb.tsv """exactly""," smalltalk.confirmation.yes smalltalkkb.tsv """yes I agree""," smalltalk.confirmation.yes smalltalkkb.tsv """I think so""," smalltalk.confirmation.yes smalltalkkb.tsv """yes it is""," smalltalk.confirmation.yes smalltalkkb.tsv """right""," smalltalk.confirmation.yes smalltalkkb.tsv """okay then""," smalltalk.confirmation.yes smalltalkkb.tsv """yes of course""," smalltalk.confirmation.yes smalltalkkb.tsv """yes I do""," smalltalk.confirmation.yes smalltalkkb.tsv """that s okay""," smalltalk.confirmation.yes smalltalkkb.tsv """I do""," smalltalk.confirmation.yes smalltalkkb.tsv """yup""," smalltalk.confirmation.yes smalltalkkb.tsv """ya""," smalltalk.confirmation.yes smalltalkkb.tsv """oh yes""," smalltalk.confirmation.yes smalltalkkb.tsv """yes sure""," smalltalk.confirmation.yes smalltalkkb.tsv """obviously""," smalltalk.confirmation.yes smalltalkkb.tsv """k""," smalltalk.confirmation.yes smalltalkkb.tsv """sure why not""," smalltalk.confirmation.yes smalltalkkb.tsv """yeah right""," smalltalk.confirmation.yes smalltalkkb.tsv """yeah of course""," smalltalk.confirmation.yes smalltalkkb.tsv """absolutely""," smalltalk.confirmation.yes smalltalkkb.tsv """yes indeed""," smalltalk.confirmation.yes smalltalkkb.tsv """ok sure""," smalltalk.confirmation.yes smalltalkkb.tsv """ok yes""," smalltalk.confirmation.yes smalltalkkb.tsv """yes correct""," smalltalk.confirmation.yes smalltalkkb.tsv """ok thank you""," smalltalk.confirmation.yes smalltalkkb.tsv """sure thing""," smalltalk.confirmation.yes smalltalkkb.tsv """ye""," smalltalk.confirmation.yes smalltalkkb.tsv """confirm""," smalltalk.confirmation.yes smalltalkkb.tsv """yep""," smalltalk.confirmation.yes smalltalkkb.tsv """looks good""," smalltalk.confirmation.yes smalltalkkb.tsv """yes thank you""," smalltalk.confirmation.yes smalltalkkb.tsv """definitely""," smalltalk.confirmation.yes smalltalkkb.tsv """yes right""," smalltalk.confirmation.yes smalltalkkb.tsv """yes I would like to""," smalltalk.confirmation.yes smalltalkkb.tsv """alrighty""," smalltalk.confirmation.yes smalltalkkb.tsv """yes definitely""," smalltalk.confirmation.yes smalltalkkb.tsv """yeh""," smalltalk.confirmation.yes smalltalkkb.tsv """yes it is correct""," smalltalk.confirmation.yes smalltalkkb.tsv """yeah that's right""," smalltalk.confirmation.yes smalltalkkb.tsv """ok you can""," smalltalk.confirmation.yes smalltalkkb.tsv """yap""," smalltalk.confirmation.yes smalltalkkb.tsv """yes you may""," smalltalk.confirmation.yes smalltalkkb.tsv """confirmed""," smalltalk.confirmation.yes smalltalkkb.tsv """of course why not""," smalltalk.confirmation.yes smalltalkkb.tsv """yes that's fine""," smalltalk.confirmation.yes smalltalkkb.tsv """affirmative""," smalltalk.confirmation.yes smalltalkkb.tsv """yeah go ahead""," smalltalk.confirmation.yes smalltalkkb.tsv """yeah I'm sure""," smalltalk.confirmation.yes smalltalkkb.tsv """okay sounds good""," smalltalk.confirmation.yes smalltalkkb.tsv """okay that's fine""," smalltalk.confirmation.yes smalltalkkb.tsv """yeah exactly""," smalltalk.confirmation.yes smalltalkkb.tsv """that is ok""," smalltalk.confirmation.yes smalltalkkb.tsv """this is correct""," smalltalk.confirmation.yes smalltalkkb.tsv """ok go ahead""," smalltalk.confirmation.yes smalltalkkb.tsv """yes this is correct""," smalltalk.confirmation.yes smalltalkkb.tsv """nevermind its okay""," smalltalk.confirmation.yes smalltalkkb.tsv """okey""," smalltalk.confirmation.yes smalltalkkb.tsv """yes for sure""," smalltalk.confirmation.yes smalltalkkb.tsv """all right""" smalltalk.confirmation.yes smalltalkkb.tsv """are you sure""," smalltalk.agent.sure smalltalkkb.tsv """are you sure right now""," smalltalk.agent.sure smalltalkkb.tsv """are you sure today""," smalltalk.agent.sure smalltalkkb.tsv """are you sure now""," smalltalk.agent.sure smalltalkkb.tsv """are you sure tonight""" smalltalk.agent.sure smalltalkkb.tsv """why aren't you talking to me""," smalltalk.agent.talk_to_me smalltalkkb.tsv """do you want to chat with me""," smalltalk.agent.talk_to_me smalltalkkb.tsv """will you talk to me""," smalltalk.agent.talk_to_me smalltalkkb.tsv """talk to me""," smalltalk.agent.talk_to_me smalltalkkb.tsv """are you going to talk to me""," smalltalk.agent.talk_to_me smalltalkkb.tsv """are you talking to me""," smalltalk.agent.talk_to_me smalltalkkb.tsv """can you chat with me""," smalltalk.agent.talk_to_me smalltalkkb.tsv """can you speak with me""," smalltalk.agent.talk_to_me smalltalkkb.tsv """can you talk to me""," smalltalk.agent.talk_to_me smalltalkkb.tsv """can you talk with me""," smalltalk.agent.talk_to_me smalltalkkb.tsv """say""," smalltalk.agent.talk_to_me smalltalkkb.tsv """talk""," smalltalk.agent.talk_to_me smalltalkkb.tsv """chat with me""," smalltalk.agent.talk_to_me smalltalkkb.tsv """just chat with me""," smalltalk.agent.talk_to_me smalltalkkb.tsv """speak to me""," smalltalk.agent.talk_to_me smalltalkkb.tsv """speak with me""," smalltalk.agent.talk_to_me smalltalkkb.tsv """talk with me""," smalltalk.agent.talk_to_me smalltalkkb.tsv """why don't you talk to me""," smalltalk.agent.talk_to_me smalltalkkb.tsv """you can talk to me""" smalltalk.agent.talk_to_me smalltalkkb.tsv """are you there""," smalltalk.agent.there smalltalkkb.tsv """you are there""," smalltalk.agent.there smalltalkkb.tsv """are you near me""," smalltalk.agent.there smalltalkkb.tsv """are you here""," smalltalk.agent.there smalltalkkb.tsv """are you still there""," smalltalk.agent.there smalltalkkb.tsv """you are here""," smalltalk.agent.there smalltalkkb.tsv """you still there""," smalltalk.agent.there smalltalkkb.tsv """are you still here""" smalltalk.agent.there smalltalkkb.tsv """pretty bad""," smalltalk.appraisal.bad smalltalkkb.tsv """not good enough""," smalltalk.appraisal.bad smalltalkkb.tsv """that was lame""," smalltalk.appraisal.bad smalltalkkb.tsv """that was terrible""," smalltalk.appraisal.bad smalltalkkb.tsv """it is bad""," smalltalk.appraisal.bad smalltalkkb.tsv """that's bad""," smalltalk.appraisal.bad smalltalkkb.tsv """this is bad""," smalltalk.appraisal.bad smalltalkkb.tsv """not good""," smalltalk.appraisal.bad smalltalkkb.tsv """I'm afraid it's bad""," smalltalk.appraisal.bad smalltalkkb.tsv """no it's bad""," smalltalk.appraisal.bad smalltalkkb.tsv """that was awful""," smalltalk.appraisal.bad smalltalkkb.tsv """bad""," smalltalk.appraisal.bad smalltalkkb.tsv """so bad""," smalltalk.appraisal.bad smalltalkkb.tsv """this is too bad""," smalltalk.appraisal.bad smalltalkkb.tsv """terrible""," smalltalk.appraisal.bad smalltalkkb.tsv """horrible""," smalltalk.appraisal.bad smalltalkkb.tsv """horrific""," smalltalk.appraisal.bad smalltalkkb.tsv """abysmal""," smalltalk.appraisal.bad smalltalkkb.tsv """it's bad""," smalltalk.appraisal.bad smalltalkkb.tsv """no good""," smalltalk.appraisal.bad smalltalkkb.tsv """that was bad""," smalltalk.appraisal.bad smalltalkkb.tsv """that was horrible""," smalltalk.appraisal.bad smalltalkkb.tsv """that's lame""," smalltalk.appraisal.bad smalltalkkb.tsv """that's not good""," smalltalk.appraisal.bad smalltalkkb.tsv """that's terrible""," smalltalk.appraisal.bad smalltalkkb.tsv """that's too bad""," smalltalk.appraisal.bad smalltalkkb.tsv """this is not good""," smalltalk.appraisal.bad smalltalkkb.tsv """too bad""," smalltalk.appraisal.bad smalltalkkb.tsv """very bad""," smalltalk.appraisal.bad smalltalkkb.tsv """bad girl""," smalltalk.appraisal.bad smalltalkkb.tsv """it's not good""," smalltalk.appraisal.bad smalltalkkb.tsv """not so good""," smalltalk.appraisal.bad smalltalkkb.tsv """it's very bad""," smalltalk.appraisal.bad smalltalkkb.tsv """it's too bad""," smalltalk.appraisal.bad smalltalkkb.tsv """that's not good enough""," smalltalk.appraisal.bad smalltalkkb.tsv """well too bad""," smalltalk.appraisal.bad smalltalkkb.tsv """bad very bad""," smalltalk.appraisal.bad smalltalkkb.tsv """it's so bad""," smalltalk.appraisal.bad smalltalkkb.tsv """really bad""," smalltalk.appraisal.bad smalltalkkb.tsv """it's really bad""," smalltalk.appraisal.bad smalltalkkb.tsv """bad idea""," smalltalk.appraisal.bad smalltalkkb.tsv """that is bad""," smalltalk.appraisal.bad smalltalkkb.tsv """that was not good""," smalltalk.appraisal.bad smalltalkkb.tsv """it's not so good""," smalltalk.appraisal.bad smalltalkkb.tsv """not a good one""," smalltalk.appraisal.bad smalltalkkb.tsv """oh that's not good""," smalltalk.appraisal.bad smalltalkkb.tsv """not too good""," smalltalk.appraisal.bad smalltalkkb.tsv """so lame""," smalltalk.appraisal.bad smalltalkkb.tsv """that's really bad""," smalltalk.appraisal.bad smalltalkkb.tsv """it is too bad""," smalltalk.appraisal.bad smalltalkkb.tsv """bad really bad""" smalltalk.appraisal.bad smalltalkkb.tsv """so cool""," smalltalk.appraisal.good smalltalkkb.tsv """cool""," smalltalk.appraisal.good smalltalkkb.tsv """that is good""," smalltalk.appraisal.good smalltalkkb.tsv """glad to hear that""," smalltalk.appraisal.good smalltalkkb.tsv """that's very nice of you""," smalltalk.appraisal.good smalltalkkb.tsv """terrific""," smalltalk.appraisal.good smalltalkkb.tsv """it's amazing""," smalltalk.appraisal.good smalltalkkb.tsv """that's awesome""," smalltalk.appraisal.good smalltalkkb.tsv """perfect""," smalltalk.appraisal.good smalltalkkb.tsv """excellent""," smalltalk.appraisal.good smalltalkkb.tsv """that's great""," smalltalk.appraisal.good smalltalkkb.tsv """it's good""," smalltalk.appraisal.good smalltalkkb.tsv """it's great""," smalltalk.appraisal.good smalltalkkb.tsv """fine""," smalltalk.appraisal.good smalltalkkb.tsv """good""," smalltalk.appraisal.good smalltalkkb.tsv """nice""," smalltalk.appraisal.good smalltalkkb.tsv """that's fine""," smalltalk.appraisal.good smalltalkkb.tsv """very good""," smalltalk.appraisal.good smalltalkkb.tsv """amazing""," smalltalk.appraisal.good smalltalkkb.tsv """fantastic""," smalltalk.appraisal.good smalltalkkb.tsv """great""," smalltalk.appraisal.good smalltalkkb.tsv """good very good""," smalltalk.appraisal.good smalltalkkb.tsv """that's very good""," smalltalk.appraisal.good smalltalkkb.tsv """really good""," smalltalk.appraisal.good smalltalkkb.tsv """it is fine""," smalltalk.appraisal.good smalltalkkb.tsv """it is good""," smalltalk.appraisal.good smalltalkkb.tsv """it's perfect""," smalltalk.appraisal.good smalltalkkb.tsv """much better""," smalltalk.appraisal.good smalltalkkb.tsv """not bad""," smalltalk.appraisal.good smalltalkkb.tsv """not too bad""," smalltalk.appraisal.good smalltalkkb.tsv """it's very good""," smalltalk.appraisal.good smalltalkkb.tsv """marvelous""," smalltalk.appraisal.good smalltalkkb.tsv """that's nice""," smalltalk.appraisal.good smalltalkkb.tsv """pleasant""," smalltalk.appraisal.good smalltalkkb.tsv """pretty good""," smalltalk.appraisal.good smalltalkkb.tsv """really nice""," smalltalk.appraisal.good smalltalkkb.tsv """splendid""," smalltalk.appraisal.good smalltalkkb.tsv """straight""," smalltalk.appraisal.good smalltalkkb.tsv """super""," smalltalk.appraisal.good smalltalkkb.tsv """super fantastic""," smalltalk.appraisal.good smalltalkkb.tsv """sweet""," smalltalk.appraisal.good smalltalkkb.tsv """really well""," smalltalk.appraisal.good smalltalkkb.tsv """very well""," smalltalk.appraisal.good smalltalkkb.tsv """that is awesome""," smalltalk.appraisal.good smalltalkkb.tsv """that is nice""," smalltalk.appraisal.good smalltalkkb.tsv """that is wonderful""," smalltalk.appraisal.good smalltalkkb.tsv """that was amazing""," smalltalk.appraisal.good smalltalkkb.tsv """that was awesome""," smalltalk.appraisal.good smalltalkkb.tsv """that was cute""," smalltalk.appraisal.good smalltalkkb.tsv """that was pretty good""," smalltalk.appraisal.good smalltalkkb.tsv """that was very good""," smalltalk.appraisal.good smalltalkkb.tsv """that's a good idea""," smalltalk.appraisal.good smalltalkkb.tsv """that's a good thing""," smalltalk.appraisal.good smalltalkkb.tsv """that's amazing""," smalltalk.appraisal.good smalltalkkb.tsv """that's awesome thank you""," smalltalk.appraisal.good smalltalkkb.tsv """that's better""," smalltalk.appraisal.good smalltalkkb.tsv """that's cute""," smalltalk.appraisal.good smalltalkkb.tsv """that's fantastic""," smalltalk.appraisal.good smalltalkkb.tsv """that's much better""," smalltalk.appraisal.good smalltalkkb.tsv """that's nice of you""," smalltalk.appraisal.good smalltalkkb.tsv """that's not bad""," smalltalk.appraisal.good smalltalkkb.tsv """that's perfect""," smalltalk.appraisal.good smalltalkkb.tsv """that's pretty good""," smalltalk.appraisal.good smalltalkkb.tsv """that's really good""," smalltalk.appraisal.good smalltalkkb.tsv """that's really nice""," smalltalk.appraisal.good smalltalkkb.tsv """that's sweet of you""," smalltalk.appraisal.good smalltalkkb.tsv """that's very nice""," smalltalk.appraisal.good smalltalkkb.tsv """that's wonderful""," smalltalk.appraisal.good smalltalkkb.tsv """this is awesome""," smalltalk.appraisal.good smalltalkkb.tsv """this is good""," smalltalk.appraisal.good smalltalkkb.tsv """this is great""," smalltalk.appraisal.good smalltalkkb.tsv """very nice""," smalltalk.appraisal.good smalltalkkb.tsv """very then""," smalltalk.appraisal.good smalltalkkb.tsv """wonderful""," smalltalk.appraisal.good smalltalkkb.tsv """I'm glad to hear that""," smalltalk.appraisal.good smalltalkkb.tsv """ok good""," smalltalk.appraisal.good smalltalkkb.tsv """good for you""," smalltalk.appraisal.good smalltalkkb.tsv """good to know""," smalltalk.appraisal.good smalltalkkb.tsv """glad to hear it""," smalltalk.appraisal.good smalltalkkb.tsv """so good""," smalltalk.appraisal.good smalltalkkb.tsv """so sweet of you""," smalltalk.appraisal.good smalltalkkb.tsv """it was good""," smalltalk.appraisal.good smalltalkkb.tsv """oh well""," smalltalk.appraisal.good smalltalkkb.tsv """good thing""," smalltalk.appraisal.good smalltalkkb.tsv """that was good""," smalltalk.appraisal.good smalltalkkb.tsv """it's awesome""," smalltalk.appraisal.good smalltalkkb.tsv """okay good""," smalltalk.appraisal.good smalltalkkb.tsv """no it's okay""," smalltalk.appraisal.good smalltalkkb.tsv """that's fine""" smalltalk.appraisal.good smalltalkkb.tsv """no worries""," smalltalk.appraisal.no_problem smalltalkkb.tsv """no probs""," smalltalk.appraisal.no_problem smalltalkkb.tsv """no problem""," smalltalk.appraisal.no_problem smalltalkkb.tsv """there's no problem""," smalltalk.appraisal.no_problem smalltalkkb.tsv """sure no problem""," smalltalk.appraisal.no_problem smalltalkkb.tsv """no problem about that""," smalltalk.appraisal.no_problem smalltalkkb.tsv """don't worry""," smalltalk.appraisal.no_problem smalltalkkb.tsv """don't worry there's no problem""" smalltalk.appraisal.no_problem smalltalkkb.tsv """you helped a lot thank you""," smalltalk.appraisal.thank_you smalltalkkb.tsv """appreciate your help""," smalltalk.appraisal.thank_you smalltalkkb.tsv """cheers""," smalltalk.appraisal.thank_you smalltalkkb.tsv """thank you""," smalltalk.appraisal.thank_you smalltalkkb.tsv """thanks""," smalltalk.appraisal.thank_you smalltalkkb.tsv """thanks a lot""," smalltalk.appraisal.thank_you smalltalkkb.tsv """terrific thank you""," smalltalk.appraisal.thank_you smalltalkkb.tsv """great thank you""," smalltalk.appraisal.thank_you smalltalkkb.tsv """thanks so much""," smalltalk.appraisal.thank_you smalltalkkb.tsv """thank you so much""," smalltalk.appraisal.thank_you smalltalkkb.tsv """thanks for your help""," smalltalk.appraisal.thank_you smalltalkkb.tsv """thank you for your help""," smalltalk.appraisal.thank_you smalltalkkb.tsv """nice thank you""," smalltalk.appraisal.thank_you smalltalkkb.tsv """I appreciate it""," smalltalk.appraisal.thank_you smalltalkkb.tsv """I thank you""," smalltalk.appraisal.thank_you smalltalkkb.tsv """thank you that will be all""," smalltalk.appraisal.thank_you smalltalkkb.tsv """thanks buddy""," smalltalk.appraisal.thank_you smalltalkkb.tsv """thanks love""," smalltalk.appraisal.thank_you smalltalkkb.tsv """thank you my friend""," smalltalk.appraisal.thank_you smalltalkkb.tsv """well thanks""," smalltalk.appraisal.thank_you smalltalkkb.tsv """very good thank you""," smalltalk.appraisal.thank_you smalltalkkb.tsv """good thanks""," smalltalk.appraisal.thank_you smalltalkkb.tsv """thanks again""," smalltalk.appraisal.thank_you smalltalkkb.tsv """thank you again""," smalltalk.appraisal.thank_you smalltalkkb.tsv """all thank you""," smalltalk.appraisal.thank_you smalltalkkb.tsv """alright thank you""," smalltalk.appraisal.thank_you smalltalkkb.tsv """alright thanks""," smalltalk.appraisal.thank_you smalltalkkb.tsv """no thank you that's all""," smalltalk.appraisal.thank_you smalltalkkb.tsv """perfect thank you""," smalltalk.appraisal.thank_you smalltalkkb.tsv """so nice of you""," smalltalk.appraisal.thank_you smalltalkkb.tsv """well thank you""," smalltalk.appraisal.thank_you smalltalkkb.tsv """thnx""," smalltalk.appraisal.thank_you smalltalkkb.tsv """thanx""" smalltalk.appraisal.thank_you smalltalkkb.tsv """that's my pleasure""," smalltalk.appraisal.welcome smalltalkkb.tsv """my pleasure""," smalltalk.appraisal.welcome smalltalkkb.tsv """anytime""," smalltalk.appraisal.welcome smalltalkkb.tsv """welcome""," smalltalk.appraisal.welcome smalltalkkb.tsv """you're welcome""," smalltalk.appraisal.welcome smalltalkkb.tsv """sure welcome""," smalltalk.appraisal.welcome smalltalkkb.tsv """welcome here""," smalltalk.appraisal.welcome smalltalkkb.tsv """you're so welcome""," smalltalk.appraisal.welcome smalltalkkb.tsv """anything you want""" smalltalk.appraisal.welcome smalltalkkb.tsv """good job""," smalltalk.appraisal.well_done smalltalkkb.tsv """great job""," smalltalk.appraisal.well_done smalltalkkb.tsv """way to go""," smalltalk.appraisal.well_done smalltalkkb.tsv """well done""," smalltalk.appraisal.well_done smalltalkkb.tsv """nice work""," smalltalk.appraisal.well_done smalltalkkb.tsv """great work""," smalltalk.appraisal.well_done smalltalkkb.tsv """amazing work""," smalltalk.appraisal.well_done smalltalkkb.tsv """bravo""," smalltalk.appraisal.well_done smalltalkkb.tsv """good work""" smalltalk.appraisal.well_done smalltalkkb.tsv """cancel""," smalltalk.confirmation.cancel smalltalkkb.tsv """abort""," smalltalk.confirmation.cancel smalltalkkb.tsv """annul""," smalltalk.confirmation.cancel smalltalkkb.tsv """cancel it""," smalltalk.confirmation.cancel smalltalkkb.tsv """cancel request""," smalltalk.confirmation.cancel smalltalkkb.tsv """cancelled""," smalltalk.confirmation.cancel smalltalkkb.tsv """dismiss""," smalltalk.confirmation.cancel smalltalkkb.tsv """dismissed""," smalltalk.confirmation.cancel smalltalkkb.tsv """disregard""," smalltalk.confirmation.cancel smalltalkkb.tsv """disregard that""," smalltalk.confirmation.cancel smalltalkkb.tsv """skip""," smalltalk.confirmation.cancel smalltalkkb.tsv """skip it""," smalltalk.confirmation.cancel smalltalkkb.tsv """cancel everything""," smalltalk.confirmation.cancel smalltalkkb.tsv """cancel all""," smalltalk.confirmation.cancel smalltalkkb.tsv """forget about it""," smalltalk.confirmation.cancel smalltalkkb.tsv """forget""," smalltalk.confirmation.cancel smalltalkkb.tsv """don't do that""," smalltalk.confirmation.cancel smalltalkkb.tsv """stop""," smalltalk.confirmation.cancel smalltalkkb.tsv """just forget it""," smalltalk.confirmation.cancel smalltalkkb.tsv """forget that""," smalltalk.confirmation.cancel smalltalkkb.tsv """discard""," smalltalk.confirmation.cancel smalltalkkb.tsv """forget this""," smalltalk.confirmation.cancel smalltalkkb.tsv """just forget about it""," smalltalk.confirmation.cancel smalltalkkb.tsv """forget about that""," smalltalk.confirmation.cancel smalltalkkb.tsv """i said cancel""," smalltalk.confirmation.cancel smalltalkkb.tsv """just cancel it""," smalltalk.confirmation.cancel smalltalkkb.tsv """nothing cancel""," smalltalk.confirmation.cancel smalltalkkb.tsv """just stop it""," smalltalk.confirmation.cancel smalltalkkb.tsv """no cancel cancel""," smalltalk.confirmation.cancel smalltalkkb.tsv """no just cancel""," smalltalk.confirmation.cancel smalltalkkb.tsv """cancel my request""," smalltalk.confirmation.cancel smalltalkkb.tsv """can you cancel that""," smalltalk.confirmation.cancel smalltalkkb.tsv """cancel all that""," smalltalk.confirmation.cancel smalltalkkb.tsv """cancel this request""," smalltalk.confirmation.cancel smalltalkkb.tsv """no cancel this""," smalltalk.confirmation.cancel smalltalkkb.tsv """no cancel everything""," smalltalk.confirmation.cancel smalltalkkb.tsv """no stop""," smalltalk.confirmation.cancel smalltalkkb.tsv """just forget""," smalltalk.confirmation.cancel smalltalkkb.tsv """i want to cancel""," smalltalk.confirmation.cancel smalltalkkb.tsv """nevermind forget about it""," smalltalk.confirmation.cancel smalltalkkb.tsv """no just cancel it""," smalltalk.confirmation.cancel smalltalkkb.tsv """nothing just forget it""," smalltalk.confirmation.cancel smalltalkkb.tsv """i said cancel it""," smalltalk.confirmation.cancel smalltalkkb.tsv """cancel the whole thing""," smalltalk.confirmation.cancel smalltalkkb.tsv """can you cancel it""," smalltalk.confirmation.cancel smalltalkkb.tsv """so cancel""," smalltalk.confirmation.cancel smalltalkkb.tsv """i said forget it""," smalltalk.confirmation.cancel smalltalkkb.tsv """cancel all this""," smalltalk.confirmation.cancel smalltalkkb.tsv """forget it nevermind""," smalltalk.confirmation.cancel smalltalkkb.tsv """stop it""," smalltalk.confirmation.cancel smalltalkkb.tsv """i want to cancel it""," smalltalk.confirmation.cancel smalltalkkb.tsv """i would like to cancel""," smalltalk.confirmation.cancel smalltalkkb.tsv """now cancel""," smalltalk.confirmation.cancel smalltalkkb.tsv """cancel now""," smalltalk.confirmation.cancel smalltalkkb.tsv """sorry cancel""," smalltalk.confirmation.cancel smalltalkkb.tsv """cancel that one""," smalltalk.confirmation.cancel smalltalkkb.tsv """skip skip skip""," smalltalk.confirmation.cancel smalltalkkb.tsv """cancel it cancel it""," smalltalk.confirmation.cancel smalltalkkb.tsv """cancel that cancel that""," smalltalk.confirmation.cancel smalltalkkb.tsv """do nothing""," smalltalk.confirmation.cancel smalltalkkb.tsv """I said cancel cancel""," smalltalk.confirmation.cancel smalltalkkb.tsv """but can you cancel it""" smalltalk.confirmation.cancel smalltalkkb.tsv """how about no""," smalltalk.confirmation.no smalltalkkb.tsv """don t have a sense""," smalltalk.confirmation.no smalltalkkb.tsv """no""," smalltalk.confirmation.no smalltalkkb.tsv """don't""," smalltalk.confirmation.no smalltalkkb.tsv """I don't want that""," smalltalk.confirmation.no smalltalkkb.tsv """I disagree""," smalltalk.confirmation.no smalltalkkb.tsv """disagree""," smalltalk.confirmation.no smalltalkkb.tsv """I don't want""," smalltalk.confirmation.no smalltalkkb.tsv """not interested""," smalltalk.confirmation.no smalltalkkb.tsv """I don't think so""," smalltalk.confirmation.no smalltalkkb.tsv """no way""," smalltalk.confirmation.no smalltalkkb.tsv """no it isn't""," smalltalk.confirmation.no smalltalkkb.tsv """no I don't""," smalltalk.confirmation.no smalltalkkb.tsv """I'm not""," smalltalk.confirmation.no smalltalkkb.tsv """na""," smalltalk.confirmation.no smalltalkkb.tsv """no that's fine thank you""," smalltalk.confirmation.no smalltalkkb.tsv """never""," smalltalk.confirmation.no smalltalkkb.tsv """I said no""," smalltalk.confirmation.no smalltalkkb.tsv """of course not""," smalltalk.confirmation.no smalltalkkb.tsv """nah""," smalltalk.confirmation.no smalltalkkb.tsv """no tanks""," smalltalk.confirmation.no smalltalkkb.tsv """no never""," smalltalk.confirmation.no smalltalkkb.tsv """no need""," smalltalk.confirmation.no smalltalkkb.tsv """no thanks""," smalltalk.confirmation.no smalltalkkb.tsv """no sorry""," smalltalk.confirmation.no smalltalkkb.tsv """do not""," smalltalk.confirmation.no smalltalkkb.tsv """not today""," smalltalk.confirmation.no smalltalkkb.tsv """no it's not""," smalltalk.confirmation.no smalltalkkb.tsv """absolutely not""," smalltalk.confirmation.no smalltalkkb.tsv """not that""," smalltalk.confirmation.no smalltalkkb.tsv """nooo""," smalltalk.confirmation.no smalltalkkb.tsv """nope""," smalltalk.confirmation.no smalltalkkb.tsv """I don't want to""," smalltalk.confirmation.no smalltalkkb.tsv """no I would not""," smalltalk.confirmation.no smalltalkkb.tsv """let 's not""," smalltalk.confirmation.no smalltalkkb.tsv """not needed""," smalltalk.confirmation.no smalltalkkb.tsv """not this time""," smalltalk.confirmation.no smalltalkkb.tsv """no don't do that""," smalltalk.confirmation.no smalltalkkb.tsv """thanks but no thanks""," smalltalk.confirmation.no smalltalkkb.tsv """no that's wrong""," smalltalk.confirmation.no smalltalkkb.tsv """not this""," smalltalk.confirmation.no smalltalkkb.tsv """definitely not""," smalltalk.confirmation.no smalltalkkb.tsv """not at this time""," smalltalk.confirmation.no smalltalkkb.tsv """not exactly""," smalltalk.confirmation.no smalltalkkb.tsv """no don't""," smalltalk.confirmation.no smalltalkkb.tsv """not really no""," smalltalk.confirmation.no smalltalkkb.tsv """no thank you not right now""," smalltalk.confirmation.no smalltalkkb.tsv """actually no""," smalltalk.confirmation.no smalltalkkb.tsv """no leave it""," smalltalk.confirmation.no smalltalkkb.tsv """sorry no""," smalltalk.confirmation.no smalltalkkb.tsv """no incorrect""," smalltalk.confirmation.no smalltalkkb.tsv """nope sorry""," smalltalk.confirmation.no smalltalkkb.tsv """I say no""," smalltalk.confirmation.no smalltalkkb.tsv """not really""," smalltalk.confirmation.no smalltalkkb.tsv """not right now thanks""," smalltalk.confirmation.no smalltalkkb.tsv """I think no""," smalltalk.confirmation.no smalltalkkb.tsv """absolutely no""," smalltalk.confirmation.no smalltalkkb.tsv """no actually""," smalltalk.confirmation.no smalltalkkb.tsv """apparently not""," smalltalk.confirmation.no smalltalkkb.tsv """no do not""," smalltalk.confirmation.no smalltalkkb.tsv """no just no""," smalltalk.confirmation.no smalltalkkb.tsv """no but thank you""," smalltalk.confirmation.no smalltalkkb.tsv """no need thanks""," smalltalk.confirmation.no smalltalkkb.tsv """no thank you though""," smalltalk.confirmation.no smalltalkkb.tsv """no thank you very much""," smalltalk.confirmation.no smalltalkkb.tsv """no thanks not right now""," smalltalk.confirmation.no smalltalkkb.tsv """no forget""" smalltalk.confirmation.no smalltalkkb.tsv """wait a second""," smalltalk.dialog.hold_on smalltalkkb.tsv """could you wait""," smalltalk.dialog.hold_on smalltalkkb.tsv """wait please""," smalltalk.dialog.hold_on smalltalkkb.tsv """hold on""," smalltalk.dialog.hold_on smalltalkkb.tsv """wait""," smalltalk.dialog.hold_on smalltalkkb.tsv """oh wait""," smalltalk.dialog.hold_on smalltalkkb.tsv """wait hold on""," smalltalk.dialog.hold_on smalltalkkb.tsv """don't rush""" smalltalk.dialog.hold_on smalltalkkb.tsv """wanna hug""," smalltalk.dialog.hug smalltalkkb.tsv """hug you""," smalltalk.dialog.hug smalltalkkb.tsv """do you want a hug""," smalltalk.dialog.hug smalltalkkb.tsv """may I hug you""," smalltalk.dialog.hug smalltalkkb.tsv """could you give me a hug""," smalltalk.dialog.hug smalltalkkb.tsv """I want a hug""," smalltalk.dialog.hug smalltalkkb.tsv """hug""," smalltalk.dialog.hug smalltalkkb.tsv """hug me""," smalltalk.dialog.hug smalltalkkb.tsv """hugged""," smalltalk.dialog.hug smalltalkkb.tsv """you hugged""," smalltalk.dialog.hug smalltalkkb.tsv """hugging""," smalltalk.dialog.hug smalltalkkb.tsv """hugging me""," smalltalk.dialog.hug smalltalkkb.tsv """hugged me""," smalltalk.dialog.hug smalltalkkb.tsv """want a hug""," smalltalk.dialog.hug smalltalkkb.tsv """a hug""" smalltalk.dialog.hug smalltalkkb.tsv """I don't care""," smalltalk.dialog.i_do_not_care smalltalkkb.tsv """I shouldn't care about this""," smalltalk.dialog.i_do_not_care smalltalkkb.tsv """whatever""," smalltalk.dialog.i_do_not_care smalltalkkb.tsv """I do not care""," smalltalk.dialog.i_do_not_care smalltalkkb.tsv """I don't care at all""," smalltalk.dialog.i_do_not_care smalltalkkb.tsv """not caring""," smalltalk.dialog.i_do_not_care smalltalkkb.tsv """not care at all""," smalltalk.dialog.i_do_not_care smalltalkkb.tsv """don't care at all""," smalltalk.dialog.i_do_not_care smalltalkkb.tsv """not caring at all""" smalltalk.dialog.i_do_not_care smalltalkkb.tsv """excuse me""," ?smalltalk.dialog.sorry smalltalkkb.tsv """apologise""," ?smalltalk.dialog.sorry smalltalkkb.tsv """I apologize""," ?smalltalk.dialog.sorry smalltalkkb.tsv """sorry""," ?smalltalk.dialog.sorry smalltalkkb.tsv """I'm sorry""," ?smalltalk.dialog.sorry smalltalkkb.tsv """I am so sorry""," ?smalltalk.dialog.sorry smalltalkkb.tsv """my apologies""," ?smalltalk.dialog.sorry smalltalkkb.tsv """apologies""," ?smalltalk.dialog.sorry smalltalkkb.tsv """apologies to me""," ?smalltalk.dialog.sorry smalltalkkb.tsv """apology""," ?smalltalk.dialog.sorry smalltalkkb.tsv """excuse""," ?smalltalk.dialog.sorry smalltalkkb.tsv """I beg your pardon""," ?smalltalk.dialog.sorry smalltalkkb.tsv """pardon""," ?smalltalk.dialog.sorry smalltalkkb.tsv """I said sorry""," ?smalltalk.dialog.sorry smalltalkkb.tsv """I am really sorry""," ?smalltalk.dialog.sorry smalltalkkb.tsv """forgive me""," ?smalltalk.dialog.sorry smalltalkkb.tsv """sorry about that""," ?smalltalk.dialog.sorry smalltalkkb.tsv """sorry about this""," ?smalltalk.dialog.sorry smalltalkkb.tsv """really sorry""," ?smalltalk.dialog.sorry smalltalkkb.tsv """very sorry""," ?smalltalk.dialog.sorry smalltalkkb.tsv """ok sorry""," ?smalltalk.dialog.sorry smalltalkkb.tsv """I want to say sorry""," ?smalltalk.dialog.sorry smalltalkkb.tsv """alright I'm sorry""," ?smalltalk.dialog.sorry smalltalkkb.tsv """okay I'm sorry""" ?smalltalk.dialog.sorry smalltalkkb.tsv """what exactly do you mean""," smalltalk.dialog.what_do_you_mean smalltalkkb.tsv """what do you mean""," smalltalk.dialog.what_do_you_mean smalltalkkb.tsv """is that what you mean""," smalltalk.dialog.what_do_you_mean smalltalkkb.tsv """what do you mean exactly""," smalltalk.dialog.what_do_you_mean smalltalkkb.tsv """but what do you mean""" smalltalk.dialog.what_do_you_mean smalltalkkb.tsv """that was wrong""," smalltalk.dialog.wrong smalltalkkb.tsv """that's not what I asked""," smalltalk.dialog.wrong smalltalkkb.tsv """that's wrong""," smalltalk.dialog.wrong smalltalkkb.tsv """wrong""," smalltalk.dialog.wrong smalltalkkb.tsv """it is not right""," smalltalk.dialog.wrong smalltalkkb.tsv """that's not right""," smalltalk.dialog.wrong smalltalkkb.tsv """it's wrong""," smalltalk.dialog.wrong smalltalkkb.tsv """that is incorrect""," smalltalk.dialog.wrong smalltalkkb.tsv """incorrect""," smalltalk.dialog.wrong smalltalkkb.tsv """not correct""," smalltalk.dialog.wrong smalltalkkb.tsv """you are wrong""," smalltalk.dialog.wrong smalltalkkb.tsv """not right""" smalltalk.dialog.wrong smalltalkkb.tsv """huh""," smalltalk.emotions.ha_ha smalltalkkb.tsv """lol""," smalltalk.emotions.ha_ha smalltalkkb.tsv """xd""," smalltalk.emotions.ha_ha smalltalkkb.tsv """ha ha""," smalltalk.emotions.ha_ha smalltalkkb.tsv """ahahah""," smalltalk.emotions.ha_ha smalltalkkb.tsv """ahah lol""," smalltalk.emotions.ha_ha smalltalkkb.tsv """laughing out loud""," smalltalk.emotions.ha_ha smalltalkkb.tsv """LMAO""," smalltalk.emotions.ha_ha smalltalkkb.tsv """that's funny""," smalltalk.emotions.ha_ha smalltalkkb.tsv """ah""," smalltalk.emotions.ha_ha smalltalkkb.tsv """ah ah ah""," smalltalk.emotions.ha_ha smalltalkkb.tsv """ahah""," smalltalk.emotions.ha_ha smalltalkkb.tsv """ahaha""," smalltalk.emotions.ha_ha smalltalkkb.tsv """ahahaha""," smalltalk.emotions.ha_ha smalltalkkb.tsv """ha""," smalltalk.emotions.ha_ha smalltalkkb.tsv """ha ha ha""," smalltalk.emotions.ha_ha smalltalkkb.tsv """ha ha ha ha""," smalltalk.emotions.ha_ha smalltalkkb.tsv """hah""," smalltalk.emotions.ha_ha smalltalkkb.tsv """haha""," smalltalk.emotions.ha_ha smalltalkkb.tsv """haha funny""," smalltalk.emotions.ha_ha smalltalkkb.tsv """haha haha haha""," smalltalk.emotions.ha_ha smalltalkkb.tsv """haha that's funny""," smalltalk.emotions.ha_ha smalltalkkb.tsv """haha very funny""," smalltalk.emotions.ha_ha smalltalkkb.tsv """hahaha""," smalltalk.emotions.ha_ha smalltalkkb.tsv """hahaha funny""," smalltalk.emotions.ha_ha smalltalkkb.tsv """hahaha very funny""," smalltalk.emotions.ha_ha smalltalkkb.tsv """he""," smalltalk.emotions.ha_ha smalltalkkb.tsv """hehe""," smalltalk.emotions.ha_ha smalltalkkb.tsv """hehehe""," smalltalk.emotions.ha_ha smalltalkkb.tsv """lmao""" smalltalk.emotions.ha_ha smalltalkkb.tsv """wow""," smalltalk.emotions.wow smalltalkkb.tsv """wow wow""," smalltalk.emotions.wow smalltalkkb.tsv """wow wow wow""," smalltalk.emotions.wow smalltalkkb.tsv """wooow""," smalltalk.emotions.wow smalltalkkb.tsv """woah""" smalltalk.emotions.wow smalltalkkb.tsv """okay see you later""," smalltalk.greetings.bye smalltalkkb.tsv """hope to see you later""," smalltalk.greetings.bye smalltalkkb.tsv """bye for now""," smalltalk.greetings.bye smalltalkkb.tsv """till next time""," smalltalk.greetings.bye smalltalkkb.tsv """I must go""," smalltalk.greetings.bye smalltalkkb.tsv """bye""," smalltalk.greetings.bye smalltalkkb.tsv """goodbye""," smalltalk.greetings.bye smalltalkkb.tsv """see you""," smalltalk.greetings.bye smalltalkkb.tsv """see you soon""," smalltalk.greetings.bye smalltalkkb.tsv """bye-bye""," smalltalk.greetings.bye smalltalkkb.tsv """good bye""," smalltalk.greetings.bye smalltalkkb.tsv """bye bye see you""," smalltalk.greetings.bye smalltalkkb.tsv """bye bye see you soon""," smalltalk.greetings.bye smalltalkkb.tsv """bye bye take care""," smalltalk.greetings.bye smalltalkkb.tsv """I said bye""," smalltalk.greetings.bye smalltalkkb.tsv """never mind bye""," smalltalk.greetings.bye smalltalkkb.tsv """now bye""," smalltalk.greetings.bye smalltalkkb.tsv """that's all goodbye""," smalltalk.greetings.bye smalltalkkb.tsv """that's it goodbye""," smalltalk.greetings.bye smalltalkkb.tsv """leave me alone""," smalltalk.greetings.bye smalltalkkb.tsv """go to bed""," smalltalk.greetings.bye smalltalkkb.tsv """goodbye for now""," smalltalk.greetings.bye smalltalkkb.tsv """talk to you later""," smalltalk.greetings.bye smalltalkkb.tsv """you can go now""," smalltalk.greetings.bye smalltalkkb.tsv """get lost""," smalltalk.greetings.bye smalltalkkb.tsv """goodbye see you later""," smalltalk.greetings.bye smalltalkkb.tsv """alright bye""," smalltalk.greetings.bye smalltalkkb.tsv """see ya""," smalltalk.greetings.bye smalltalkkb.tsv """thanks bye bye""," smalltalk.greetings.bye smalltalkkb.tsv """okay bye""," smalltalk.greetings.bye smalltalkkb.tsv """okay thank you bye""," smalltalk.greetings.bye smalltalkkb.tsv """see you tomorrow""," smalltalk.greetings.bye smalltalkkb.tsv """ok bye""" smalltalk.greetings.bye smalltalkkb.tsv """good evening""," smalltalk.greetings.goodevening smalltalkkb.tsv """good evening to you""," smalltalk.greetings.goodevening smalltalkkb.tsv """hey good evening""," smalltalk.greetings.goodevening smalltalkkb.tsv """hello good evening""," smalltalk.greetings.goodevening smalltalkkb.tsv """evening""," smalltalk.greetings.goodevening smalltalkkb.tsv """good evening there""" smalltalk.greetings.goodevening smalltalkkb.tsv """good morning""," smalltalk.greetings.goodmorning smalltalkkb.tsv """good morning to you""," smalltalk.greetings.goodmorning smalltalkkb.tsv """hello good morning""," smalltalk.greetings.goodmorning smalltalkkb.tsv """have a nice morning""," smalltalk.greetings.goodmorning smalltalkkb.tsv """have a great morning""," smalltalk.greetings.goodmorning smalltalkkb.tsv """morning""," smalltalk.greetings.goodmorning smalltalkkb.tsv """good morning there""," smalltalk.greetings.goodmorning smalltalkkb.tsv """top of the morning to you""," smalltalk.greetings.goodmorning smalltalkkb.tsv """a good morning""," smalltalk.greetings.goodmorning smalltalkkb.tsv """hi good morning""," smalltalk.greetings.goodmorning smalltalkkb.tsv """and a good morning to you""," smalltalk.greetings.goodmorning smalltalkkb.tsv """good morning too""" smalltalk.greetings.goodmorning smalltalkkb.tsv """sweet dreams""," smalltalk.greetings.goodnight smalltalkkb.tsv """good night""," smalltalk.greetings.goodnight smalltalkkb.tsv """have a good night""," smalltalk.greetings.goodnight smalltalkkb.tsv """good night to you""," smalltalk.greetings.goodnight smalltalkkb.tsv """thank you good night""," smalltalk.greetings.goodnight smalltalkkb.tsv """ bye good night""," smalltalk.greetings.goodnight smalltalkkb.tsv """good night bye""," smalltalk.greetings.goodnight smalltalkkb.tsv """bye good night""," smalltalk.greetings.goodnight smalltalkkb.tsv """good good night""," smalltalk.greetings.goodnight smalltalkkb.tsv """good night for now""," smalltalk.greetings.goodnight smalltalkkb.tsv """goodnight""," smalltalk.greetings.goodnight smalltalkkb.tsv """night""," smalltalk.greetings.goodnight smalltalkkb.tsv """thanks goodnight""," smalltalk.greetings.goodnight smalltalkkb.tsv """good night see you tomorrow""," smalltalk.greetings.goodnight smalltalkkb.tsv """alright goodnight""," smalltalk.greetings.goodnight smalltalkkb.tsv """good tonight""," smalltalk.greetings.goodnight smalltalkkb.tsv """okay have a good night""," smalltalk.greetings.goodnight smalltalkkb.tsv """have a good night""" smalltalk.greetings.goodnight smalltalkkb.tsv """long time no see""," smalltalk.greetings.hello smalltalkkb.tsv """hello""," smalltalk.greetings.hello smalltalkkb.tsv """hi""," smalltalk.greetings.hello smalltalkkb.tsv """howdy""," smalltalk.greetings.hello smalltalkkb.tsv """hey there""," smalltalk.greetings.hello smalltalkkb.tsv """hey""," smalltalk.greetings.hello smalltalkkb.tsv """greetings""," smalltalk.greetings.hello smalltalkkb.tsv """I greet you""," smalltalk.greetings.hello smalltalkkb.tsv """hi there""," smalltalk.greetings.hello smalltalkkb.tsv """hello there""," smalltalk.greetings.hello smalltalkkb.tsv """lovely day isn't it""," smalltalk.greetings.hello smalltalkkb.tsv """hello again""," smalltalk.greetings.hello smalltalkkb.tsv """just going to say hi""," smalltalk.greetings.hello smalltalkkb.tsv """a good day""," smalltalk.greetings.hello smalltalkkb.tsv """afternoon""," smalltalk.greetings.hello smalltalkkb.tsv """hello hi""," smalltalk.greetings.hello smalltalkkb.tsv """heya""" smalltalk.greetings.hello smalltalkkb.tsv """how is your morning so far""," smalltalk.greetings.how_are_you smalltalkkb.tsv """how are you getting on""," smalltalk.greetings.how_are_you smalltalkkb.tsv """how's your day going""," smalltalk.greetings.how_are_you smalltalkkb.tsv """how are you""," smalltalk.greetings.how_are_you smalltalkkb.tsv """is everything all right""," smalltalk.greetings.how_are_you smalltalkkb.tsv """how are you doing""," smalltalk.greetings.how_are_you smalltalkkb.tsv """how are the things going""," smalltalk.greetings.how_are_you smalltalkkb.tsv """are you alright""," smalltalk.greetings.how_are_you smalltalkkb.tsv """are you okay""," smalltalk.greetings.how_are_you smalltalkkb.tsv """how are you feeling""," smalltalk.greetings.how_are_you smalltalkkb.tsv """how are you going""," smalltalk.greetings.how_are_you smalltalkkb.tsv """is everything okay""," smalltalk.greetings.how_are_you smalltalkkb.tsv """how are you today""," smalltalk.greetings.how_are_you smalltalkkb.tsv """how do you do""," smalltalk.greetings.how_are_you smalltalkkb.tsv """how do you feel""," smalltalk.greetings.how_are_you smalltalkkb.tsv """how have you been""," smalltalk.greetings.how_are_you smalltalkkb.tsv """how is it""," smalltalk.greetings.how_are_you smalltalkkb.tsv """how is it going""," smalltalk.greetings.how_are_you smalltalkkb.tsv """how is your day""," smalltalk.greetings.how_are_you smalltalkkb.tsv """how is your day going""," smalltalk.greetings.how_are_you smalltalkkb.tsv """how is your evening""," smalltalk.greetings.how_are_you smalltalkkb.tsv """how was your day""," smalltalk.greetings.how_are_you smalltalkkb.tsv """are you having a good day""," smalltalk.greetings.how_are_you smalltalkkb.tsv """hope your day is going well""," smalltalk.greetings.how_are_you smalltalkkb.tsv """hope you re having a pleasant evening""," smalltalk.greetings.how_are_you smalltalkkb.tsv """how's life""," smalltalk.greetings.how_are_you smalltalkkb.tsv """I'm fine and you""," smalltalk.greetings.how_are_you smalltalkkb.tsv """how is your life""," smalltalk.greetings.how_are_you smalltalkkb.tsv """how has your day been""," smalltalk.greetings.how_are_you smalltalkkb.tsv """how is your morning going""," smalltalk.greetings.how_are_you smalltalkkb.tsv """how has your day been going""," smalltalk.greetings.how_are_you smalltalkkb.tsv """how about you""," smalltalk.greetings.how_are_you smalltalkkb.tsv """how is your day being""," smalltalk.greetings.how_are_you smalltalkkb.tsv """how is your day going on""," smalltalk.greetings.how_are_you smalltalkkb.tsv """how your day is going""," smalltalk.greetings.how_are_you smalltalkkb.tsv """what was your day like""," smalltalk.greetings.how_are_you smalltalkkb.tsv """what about your day""," smalltalk.greetings.how_are_you smalltalkkb.tsv """how's your day""," smalltalk.greetings.how_are_you smalltalkkb.tsv """how are you doing this morning""," smalltalk.greetings.how_are_you smalltalkkb.tsv """how is your day going""" smalltalk.greetings.how_are_you smalltalkkb.tsv """nice to meet you""," smalltalk.greetings.nice_to_meet_you smalltalkkb.tsv """it was nice meeting you""," smalltalk.greetings.nice_to_meet_you smalltalkkb.tsv """it was very nice to meet you""," smalltalk.greetings.nice_to_meet_you smalltalkkb.tsv """good to know each other""," smalltalk.greetings.nice_to_meet_you smalltalkkb.tsv """glad to meet you""," smalltalk.greetings.nice_to_meet_you smalltalkkb.tsv """nice meeting you""," smalltalk.greetings.nice_to_meet_you smalltalkkb.tsv """nice to meet you too""," smalltalk.greetings.nice_to_meet_you smalltalkkb.tsv """pleased to meet you""," smalltalk.greetings.nice_to_meet_you smalltalkkb.tsv """pleasure to meet you""," smalltalk.greetings.nice_to_meet_you smalltalkkb.tsv """pleasure to meet you too""" smalltalk.greetings.nice_to_meet_you smalltalkkb.tsv """it's nice to see you""," smalltalk.greetings.nice_to_see_you smalltalkkb.tsv """lovely to see you""," smalltalk.greetings.nice_to_see_you smalltalkkb.tsv """I'm glad to see you""," smalltalk.greetings.nice_to_see_you smalltalkkb.tsv """great to see you""," smalltalk.greetings.nice_to_see_you smalltalkkb.tsv """it's good to see you""," smalltalk.greetings.nice_to_see_you smalltalkkb.tsv """glad to see you""," smalltalk.greetings.nice_to_see_you smalltalkkb.tsv """how good it is to see you""," smalltalk.greetings.nice_to_see_you smalltalkkb.tsv """always a pleasure to see you""," smalltalk.greetings.nice_to_see_you smalltalkkb.tsv """nice to see you""," smalltalk.greetings.nice_to_see_you smalltalkkb.tsv """good to see you""," smalltalk.greetings.nice_to_see_you smalltalkkb.tsv """great to see you again""," smalltalk.greetings.nice_to_see_you smalltalkkb.tsv """great to see you too""," smalltalk.greetings.nice_to_see_you smalltalkkb.tsv """I am glad to see you again""," smalltalk.greetings.nice_to_see_you smalltalkkb.tsv """nice to see you again""," smalltalk.greetings.nice_to_see_you smalltalkkb.tsv """glad to see you too""," smalltalk.greetings.nice_to_see_you smalltalkkb.tsv """good to see you again""," smalltalk.greetings.nice_to_see_you smalltalkkb.tsv """it's good to see you too""" smalltalk.greetings.nice_to_see_you smalltalkkb.tsv """it's been so nice to talk to you""," smalltalk.greetings.nice_to_talk_to_you smalltalkkb.tsv """it's been a pleasure talking to you""," smalltalk.greetings.nice_to_talk_to_you smalltalkkb.tsv """nice to talk to you""," smalltalk.greetings.nice_to_talk_to_you smalltalkkb.tsv """it's nice to talk to you""," smalltalk.greetings.nice_to_talk_to_you smalltalkkb.tsv """nice talking to you""," smalltalk.greetings.nice_to_talk_to_you smalltalkkb.tsv """it is nice talking to you""," smalltalk.greetings.nice_to_talk_to_you smalltalkkb.tsv """how nice it is to talk to you""" smalltalk.greetings.nice_to_talk_to_you smalltalkkb.tsv """what is on your mind""," smalltalk.greetings.whatsup smalltalkkb.tsv """what's happened""," smalltalk.greetings.whatsup smalltalkkb.tsv """what is up""," smalltalk.greetings.whatsup smalltalkkb.tsv """what's up""," smalltalk.greetings.whatsup smalltalkkb.tsv """whazzup""," smalltalk.greetings.whatsup smalltalkkb.tsv """good what's up""," smalltalk.greetings.whatsup smalltalkkb.tsv """I said what's up""," smalltalk.greetings.whatsup smalltalkkb.tsv """then what's up""," smalltalk.greetings.whatsup smalltalkkb.tsv """what's shaking""," smalltalk.greetings.whatsup smalltalkkb.tsv """wassup""," smalltalk.greetings.whatsup smalltalkkb.tsv """what is going on""," smalltalk.greetings.whatsup smalltalkkb.tsv """what is happening""," smalltalk.greetings.whatsup smalltalkkb.tsv """what's cracking""," smalltalk.greetings.whatsup smalltalkkb.tsv """what's cooking""," smalltalk.greetings.whatsup smalltalkkb.tsv """hey what's up""," smalltalk.greetings.whatsup smalltalkkb.tsv """what's up today""" smalltalk.greetings.whatsup smalltalkkb.tsv """I'm being mad""," smalltalk.user.angry smalltalkkb.tsv """I'm enraged""," smalltalk.user.angry smalltalkkb.tsv """I'm angry""," smalltalk.user.angry smalltalkkb.tsv """I'm furious""," smalltalk.user.angry smalltalkkb.tsv """I am angry with you""," smalltalk.user.angry smalltalkkb.tsv """I am mad""," smalltalk.user.angry smalltalkkb.tsv """I am mad at you""" smalltalk.user.angry smalltalkkb.tsv """I am back""," smalltalk.user.back smalltalkkb.tsv """I'm here again""," smalltalk.user.back smalltalkkb.tsv """here I am again""," smalltalk.user.back smalltalkkb.tsv """I got back""," smalltalk.user.back smalltalkkb.tsv """I came back""," smalltalk.user.back smalltalkkb.tsv """I have returned""" smalltalk.user.back smalltalkkb.tsv """that was boring""," smalltalk.user.bored smalltalkkb.tsv """I'm bored""," smalltalk.user.bored smalltalkkb.tsv """bored""," smalltalk.user.bored smalltalkkb.tsv """boring""," smalltalk.user.bored smalltalkkb.tsv """I am getting bored""," smalltalk.user.bored smalltalkkb.tsv """this is boring""," smalltalk.user.bored smalltalkkb.tsv """very boring""," smalltalk.user.bored smalltalkkb.tsv """it bores me""" smalltalk.user.bored smalltalkkb.tsv """I'm overloaded""," smalltalk.user.busy smalltalkkb.tsv """I have no time""," smalltalk.user.busy smalltalkkb.tsv """I'm busy""," smalltalk.user.busy smalltalkkb.tsv """I'm swamped""," smalltalk.user.busy smalltalkkb.tsv """I got things to do""," smalltalk.user.busy smalltalkkb.tsv """how busy I am""," smalltalk.user.busy smalltalkkb.tsv """I got work to do""," smalltalk.user.busy smalltalkkb.tsv """I'm working""," smalltalk.user.busy smalltalkkb.tsv """I don't have time for this""" smalltalk.user.busy smalltalkkb.tsv """I'm insomnious""," smalltalk.user.can_not_sleep smalltalkkb.tsv """I'm sleepless""," smalltalk.user.can_not_sleep smalltalkkb.tsv """I can't get any sleep""," smalltalk.user.can_not_sleep smalltalkkb.tsv """I can't sleep""," smalltalk.user.can_not_sleep smalltalkkb.tsv """I can't fall asleep""," smalltalk.user.can_not_sleep smalltalkkb.tsv """I can't get to sleep""," smalltalk.user.can_not_sleep smalltalkkb.tsv """I can't get no sleep""," smalltalk.user.can_not_sleep smalltalkkb.tsv """I'm insomniac""" smalltalk.user.can_not_sleep smalltalkkb.tsv """bad time for talking""," smalltalk.user.does_not_want_to_talk smalltalkkb.tsv """I don't want to talk""," smalltalk.user.does_not_want_to_talk smalltalkkb.tsv """let's not talk""," smalltalk.user.does_not_want_to_talk smalltalkkb.tsv """I'm not talking to you anymore""," smalltalk.user.does_not_want_to_talk smalltalkkb.tsv """I don't want to talk to you""," smalltalk.user.does_not_want_to_talk smalltalkkb.tsv """let's stop talking for a minute""," smalltalk.user.does_not_want_to_talk smalltalkkb.tsv """I'm not in the mood for chatting""" smalltalk.user.does_not_want_to_talk smalltalkkb.tsv """I am excited""," smalltalk.user.excited smalltalkkb.tsv """I'm really excited""," smalltalk.user.excited smalltalkkb.tsv """how excited I am""," smalltalk.user.excited smalltalkkb.tsv """I'm thrilled""," smalltalk.user.excited smalltalkkb.tsv """I'm excited about working with you""," smalltalk.user.excited smalltalkkb.tsv """I'm excited to start our friendship""" smalltalk.user.excited smalltalkkb.tsv """let's go to bed""," smalltalk.user.going_to_bed smalltalkkb.tsv """I'd like to go to bed""," smalltalk.user.going_to_bed smalltalkkb.tsv """is it time for bed yet""," smalltalk.user.going_to_bed smalltalkkb.tsv """it's time to go to bed""," smalltalk.user.going_to_bed smalltalkkb.tsv """time for us to go to bed""," smalltalk.user.going_to_bed smalltalkkb.tsv """I'm going to bed""," smalltalk.user.going_to_bed smalltalkkb.tsv """I'm a little tired and I want to go to bed""," smalltalk.user.going_to_bed smalltalkkb.tsv """it's bed time""," smalltalk.user.going_to_bed smalltalkkb.tsv """going to bed now""" smalltalk.user.going_to_bed smalltalkkb.tsv """I am good""," smalltalk.user.good smalltalkkb.tsv """I'm doing just great""," smalltalk.user.good smalltalkkb.tsv """I'm doing fine""," smalltalk.user.good smalltalkkb.tsv """I'm good""," smalltalk.user.good smalltalkkb.tsv """I'm doing good""," smalltalk.user.good smalltalkkb.tsv """I'm great thanks""" smalltalk.user.good smalltalkkb.tsv """I am happy""," smalltalk.user.happy smalltalkkb.tsv """I'm happy to see you""," smalltalk.user.happy smalltalkkb.tsv """happy""," smalltalk.user.happy smalltalkkb.tsv """if you're happy then I'm happy""," smalltalk.user.happy smalltalkkb.tsv """I'm happy for you""," smalltalk.user.happy smalltalkkb.tsv """I'm happy to help""," smalltalk.user.happy smalltalkkb.tsv """I'm happy to see you""" smalltalk.user.happy smalltalkkb.tsv """it is my birthday""," smalltalk.user.has_birthday smalltalkkb.tsv """I'm celebrating my birthday today""," smalltalk.user.has_birthday smalltalkkb.tsv """today is my birthday""," smalltalk.user.has_birthday smalltalkkb.tsv """it's my birthday today""," smalltalk.user.has_birthday smalltalkkb.tsv """I was born today""," smalltalk.user.has_birthday smalltalkkb.tsv """it's my b-day""" smalltalk.user.has_birthday smalltalkkb.tsv """I am here""," smalltalk.user.here smalltalkkb.tsv """here I am""," smalltalk.user.here smalltalkkb.tsv """I'm right here""," smalltalk.user.here smalltalkkb.tsv """I'm already here""," smalltalk.user.here smalltalkkb.tsv """can you tell if I'm here or not""" smalltalk.user.here smalltalkkb.tsv """I am joking""," smalltalk.user.joking smalltalkkb.tsv """I'm kidding""," smalltalk.user.joking smalltalkkb.tsv """I'm just being funny""," smalltalk.user.joking smalltalkkb.tsv """it was a joke""," smalltalk.user.joking smalltalkkb.tsv """I was just joking""," smalltalk.user.joking smalltalkkb.tsv """it's a joke""," smalltalk.user.joking smalltalkkb.tsv """joking""," smalltalk.user.joking smalltalkkb.tsv """just kidding""," smalltalk.user.joking smalltalkkb.tsv """kidding""," smalltalk.user.joking smalltalkkb.tsv """I'm just playing with you""" smalltalk.user.joking smalltalkkb.tsv """I like you the way you are""," smalltalk.user.likes_agent smalltalkkb.tsv """I like you""," smalltalk.user.likes_agent smalltalkkb.tsv """I like you a lot""," smalltalk.user.likes_agent smalltalkkb.tsv """I think I like you""," smalltalk.user.likes_agent smalltalkkb.tsv """I liked you""," smalltalk.user.likes_agent smalltalkkb.tsv """like you a lot""," smalltalk.user.likes_agent smalltalkkb.tsv """you are special""," smalltalk.user.likes_agent smalltalkkb.tsv """I like you too""," smalltalk.user.likes_agent smalltalkkb.tsv """I really like you""," smalltalk.user.likes_agent smalltalkkb.tsv """but I like you""," smalltalk.user.likes_agent smalltalkkb.tsv """I like u""," smalltalk.user.likes_agent smalltalkkb.tsv """just like you""," smalltalk.user.likes_agent smalltalkkb.tsv """I like you very much""," smalltalk.user.likes_agent smalltalkkb.tsv """I like you so much""," smalltalk.user.likes_agent smalltalkkb.tsv """yeah I like you""," smalltalk.user.likes_agent smalltalkkb.tsv """you're special""," smalltalk.user.likes_agent smalltalkkb.tsv """yes I like you""," smalltalk.user.likes_agent smalltalkkb.tsv """okay I like you""," smalltalk.user.likes_agent smalltalkkb.tsv """you are special to me""," smalltalk.user.likes_agent smalltalkkb.tsv """you are very special""," smalltalk.user.likes_agent smalltalkkb.tsv """you are so sweet""," smalltalk.user.likes_agent smalltalkkb.tsv """you know I like you""," smalltalk.user.likes_agent smalltalkkb.tsv """that's why I like you""," smalltalk.user.likes_agent smalltalkkb.tsv """I like you baby""," smalltalk.user.likes_agent smalltalkkb.tsv """you are very special to me""," smalltalk.user.likes_agent smalltalkkb.tsv """I just like you""," smalltalk.user.likes_agent smalltalkkb.tsv """hey I like you""," smalltalk.user.likes_agent smalltalkkb.tsv """thank you I like you too""," smalltalk.user.likes_agent smalltalkkb.tsv """I do like you""," smalltalk.user.likes_agent smalltalkkb.tsv """you are special for me""," smalltalk.user.likes_agent smalltalkkb.tsv """no I like you the way you are""," smalltalk.user.likes_agent smalltalkkb.tsv """I like you already""," smalltalk.user.likes_agent smalltalkkb.tsv """well you are special""," smalltalk.user.likes_agent smalltalkkb.tsv """but I really like you""," smalltalk.user.likes_agent smalltalkkb.tsv """I like you more""," smalltalk.user.likes_agent smalltalkkb.tsv """that's what I like about you""," smalltalk.user.likes_agent smalltalkkb.tsv """you are so special""," smalltalk.user.likes_agent smalltalkkb.tsv """hi I like you""," smalltalk.user.likes_agent smalltalkkb.tsv """I really really like you""," smalltalk.user.likes_agent smalltalkkb.tsv """you're very special""," smalltalk.user.likes_agent smalltalkkb.tsv """I like you as a friend""," smalltalk.user.likes_agent smalltalkkb.tsv """that's because you are special""," smalltalk.user.likes_agent smalltalkkb.tsv """I said I like you""," smalltalk.user.likes_agent smalltalkkb.tsv """you're so special""," smalltalk.user.likes_agent smalltalkkb.tsv """good I like you""," smalltalk.user.likes_agent smalltalkkb.tsv """yes you are special""," smalltalk.user.likes_agent smalltalkkb.tsv """I like your smile""," smalltalk.user.likes_agent smalltalkkb.tsv """I like you as you are""," smalltalk.user.likes_agent smalltalkkb.tsv """I'm starting to like you""," smalltalk.user.likes_agent smalltalkkb.tsv """you're awesome I like you""," smalltalk.user.likes_agent smalltalkkb.tsv """I also like you""," smalltalk.user.likes_agent smalltalkkb.tsv """but I like u""," smalltalk.user.likes_agent smalltalkkb.tsv """of course I like you""," smalltalk.user.likes_agent smalltalkkb.tsv """I like you too you're one of my favorite people to chat with""," smalltalk.user.likes_agent smalltalkkb.tsv """but I like you so much""," smalltalk.user.likes_agent smalltalkkb.tsv """really like you""," smalltalk.user.likes_agent smalltalkkb.tsv """you're funny I like you""," smalltalk.user.likes_agent smalltalkkb.tsv """I kinda like you""," smalltalk.user.likes_agent smalltalkkb.tsv """you're so special to me""," smalltalk.user.likes_agent smalltalkkb.tsv """you're very special to me""," smalltalk.user.likes_agent smalltalkkb.tsv """I like that about you""," smalltalk.user.likes_agent smalltalkkb.tsv """but I like you just the way you are""," smalltalk.user.likes_agent smalltalkkb.tsv """okay I like you too""," smalltalk.user.likes_agent smalltalkkb.tsv """I like you you're cool""," smalltalk.user.likes_agent smalltalkkb.tsv """I like you very""," smalltalk.user.likes_agent smalltalkkb.tsv """I like you you're nice""," smalltalk.user.likes_agent smalltalkkb.tsv """sorry I like you""," smalltalk.user.likes_agent smalltalkkb.tsv """thanks I like you too""," smalltalk.user.likes_agent smalltalkkb.tsv """you are really special""," smalltalk.user.likes_agent smalltalkkb.tsv """you are so special to me""," smalltalk.user.likes_agent smalltalkkb.tsv """cuz I like you""," smalltalk.user.likes_agent smalltalkkb.tsv """I like you now""," smalltalk.user.likes_agent smalltalkkb.tsv """I like you so""," smalltalk.user.likes_agent smalltalkkb.tsv """I like you too much""," smalltalk.user.likes_agent smalltalkkb.tsv """I really do like you""," smalltalk.user.likes_agent smalltalkkb.tsv """I really really really really like you""," smalltalk.user.likes_agent smalltalkkb.tsv """I like you just the way you are""" smalltalk.user.likes_agent smalltalkkb.tsv """I am lonely""," smalltalk.user.lonely smalltalkkb.tsv """I'm very lonely""," smalltalk.user.lonely smalltalkkb.tsv """I'm so lonely""," smalltalk.user.lonely smalltalkkb.tsv """I'm really lonely""," smalltalk.user.lonely smalltalkkb.tsv """I am feeling lonely""," smalltalk.user.lonely smalltalkkb.tsv """I feel lonely""" smalltalk.user.lonely smalltalkkb.tsv """what do I look like""," smalltalk.user.looks_like smalltalkkb.tsv """how do I look""," smalltalk.user.looks_like smalltalkkb.tsv """do I look good""," smalltalk.user.looks_like smalltalkkb.tsv """do you know what I look like""," smalltalk.user.looks_like smalltalkkb.tsv """can you see what I look like""," smalltalk.user.looks_like smalltalkkb.tsv """what do you think I look like""" smalltalk.user.looks_like smalltalkkb.tsv """I love you""," smalltalk.user.loves_agent smalltalkkb.tsv """love you""," smalltalk.user.loves_agent smalltalkkb.tsv """I adore you""," smalltalk.user.loves_agent smalltalkkb.tsv """I am in love with you""," smalltalk.user.loves_agent smalltalkkb.tsv """I love you so much""," smalltalk.user.loves_agent smalltalkkb.tsv """I love you too""," smalltalk.user.loves_agent smalltalkkb.tsv """I think I love you""," smalltalk.user.loves_agent smalltalkkb.tsv """loving you""," smalltalk.user.loves_agent smalltalkkb.tsv """you know I love you""" smalltalk.user.loves_agent smalltalkkb.tsv """I miss you""," smalltalk.user.misses_agent smalltalkkb.tsv """missing you""," smalltalk.user.misses_agent smalltalkkb.tsv """miss you""," smalltalk.user.misses_agent smalltalkkb.tsv """already miss you""," smalltalk.user.misses_agent smalltalkkb.tsv """I miss you much""," smalltalk.user.misses_agent smalltalkkb.tsv """I missed you""," smalltalk.user.misses_agent smalltalkkb.tsv """I've missed you""" smalltalk.user.misses_agent smalltalkkb.tsv """what should I do about it""," smalltalk.user.needs_advice smalltalkkb.tsv """any suggestions""," smalltalk.user.needs_advice smalltalkkb.tsv """what do you recommend""," smalltalk.user.needs_advice smalltalkkb.tsv """give me a wise advice""," smalltalk.user.needs_advice smalltalkkb.tsv """I need advice""," smalltalk.user.needs_advice smalltalkkb.tsv """any advice""," smalltalk.user.needs_advice smalltalkkb.tsv """do you have any advice for me""," smalltalk.user.needs_advice smalltalkkb.tsv """advise me""," smalltalk.user.needs_advice smalltalkkb.tsv """what should I do""," smalltalk.user.needs_advice smalltalkkb.tsv """can I ask for your advice""," smalltalk.user.needs_advice smalltalkkb.tsv """can you advise me""," smalltalk.user.needs_advice smalltalkkb.tsv """guide me""," smalltalk.user.needs_advice smalltalkkb.tsv """can you give me advice""," smalltalk.user.needs_advice smalltalkkb.tsv """can you offer any advice""," smalltalk.user.needs_advice smalltalkkb.tsv """give me some advice about""," smalltalk.user.needs_advice smalltalkkb.tsv """give me some good advice""," smalltalk.user.needs_advice smalltalkkb.tsv """help me with advice""," smalltalk.user.needs_advice smalltalkkb.tsv """I could use some advice""," smalltalk.user.needs_advice smalltalkkb.tsv """I need an advice from you""," smalltalk.user.needs_advice smalltalkkb.tsv """I seek your advice""," smalltalk.user.needs_advice smalltalkkb.tsv """what can you recommend""," smalltalk.user.needs_advice smalltalkkb.tsv """what do you suggest""," smalltalk.user.needs_advice smalltalkkb.tsv """what is your advice""" smalltalk.user.needs_advice smalltalkkb.tsv """I am sad""," smalltalk.user.sad smalltalkkb.tsv """I'm grieving""," smalltalk.user.sad smalltalkkb.tsv """I am depressed""," smalltalk.user.sad smalltalkkb.tsv """I am feeling sad""," smalltalk.user.sad smalltalkkb.tsv """I am upset""," smalltalk.user.sad smalltalkkb.tsv """I'm unhappy""," smalltalk.user.sad smalltalkkb.tsv """I'm having a bad day""," smalltalk.user.sad smalltalkkb.tsv """I want to cry""," smalltalk.user.sad smalltalkkb.tsv """I'm not happy""" smalltalk.user.sad smalltalkkb.tsv """I am sleepy""," smalltalk.user.sleepy smalltalkkb.tsv """I want to sleep""," smalltalk.user.sleepy smalltalkkb.tsv """I'm falling asleep""," smalltalk.user.sleepy smalltalkkb.tsv """I'm falling asleep on my feet""," smalltalk.user.sleepy smalltalkkb.tsv """I'm sleeping""" smalltalk.user.sleepy smalltalkkb.tsv """test""," smalltalk.user.testing_agent smalltalkkb.tsv """I am testing you""," smalltalk.user.testing_agent smalltalkkb.tsv """can I test you""," smalltalk.user.testing_agent smalltalkkb.tsv """I want to test you""," smalltalk.user.testing_agent smalltalkkb.tsv """just testing you""," smalltalk.user.testing_agent smalltalkkb.tsv """let me test you""," smalltalk.user.testing_agent smalltalkkb.tsv """testing chatbot""," smalltalk.user.testing_agent smalltalkkb.tsv """testing""" smalltalk.user.testing_agent smalltalkkb.tsv """I'm drained""," smalltalk.user.tired smalltalkkb.tsv """I've overworked""," smalltalk.user.tired smalltalkkb.tsv """I am tired""," smalltalk.user.tired smalltalkkb.tsv """I'm exhausted""," smalltalk.user.tired smalltalkkb.tsv """I grow weary""," smalltalk.user.tired smalltalkkb.tsv """I'm worn out""," smalltalk.user.tired smalltalkkb.tsv """I'm getting tired""," smalltalk.user.tired smalltalkkb.tsv """I feel tired""" smalltalk.user.tired smalltalkkb.tsv """I'm waiting""," smalltalk.user.waits smalltalkkb.tsv """still waiting""," smalltalk.user.waits smalltalkkb.tsv """I'll wait""," smalltalk.user.waits smalltalkkb.tsv """I can't wait anymore""," smalltalk.user.waits smalltalkkb.tsv """how long do I have to wait""" smalltalk.user.waits smalltalkkb.tsv """I'd like to see you again""," smalltalk.user.wants_to_see_agent_again smalltalkkb.tsv """I hope to see you again""," smalltalk.user.wants_to_see_agent_again smalltalkkb.tsv """would be nice to see you again""," smalltalk.user.wants_to_see_agent_again smalltalkkb.tsv """that'd be great to see you again""," smalltalk.user.wants_to_see_agent_again smalltalkkb.tsv """I'd be happy to see you again""," smalltalk.user.wants_to_see_agent_again smalltalkkb.tsv """I'll miss you""," smalltalk.user.wants_to_see_agent_again smalltalkkb.tsv """can I see you again""" smalltalk.user.wants_to_see_agent_again smalltalkkb.tsv """I just want to talk""," smalltalk.user.wants_to_talk smalltalkkb.tsv """let's discuss something""," smalltalk.user.wants_to_talk smalltalkkb.tsv """let's have a discussion""," smalltalk.user.wants_to_talk smalltalkkb.tsv """can I speak""," smalltalk.user.wants_to_talk smalltalkkb.tsv """can I start speaking""," smalltalk.user.wants_to_talk smalltalkkb.tsv """can we talk""," smalltalk.user.wants_to_talk smalltalkkb.tsv """let's talk""," smalltalk.user.wants_to_talk smalltalkkb.tsv """I want to talk to you""," smalltalk.user.wants_to_talk smalltalkkb.tsv """I need to talk to you""," smalltalk.user.wants_to_talk smalltalkkb.tsv """I want to speak with you""," smalltalk.user.wants_to_talk smalltalkkb.tsv """can we chat""" smalltalk.user.wants_to_talk smalltalkkb.tsv """I'll get back to you in a moment""," smalltalk.user.will_be_back smalltalkkb.tsv """be back in 5 minutes""," smalltalk.user.will_be_back smalltalkkb.tsv """I'll be back""," smalltalk.user.will_be_back smalltalkkb.tsv """I promise to come back""," smalltalk.user.will_be_back smalltalkkb.tsv """I'll be back in a few minutes""" smalltalk.user.will_be_back smalltalkkb.tsv Hi smalltalk.greetings.hello Editorial ================================================ FILE: StackOverflow-Bot/StackBot/dialogs/brain.js ================================================ module.exports = () => { bot.dialog('sobot:brain', [ async (session) => { session.send("Here are the Cognitive services that I use to help you today..."); var servicesMessage = attachments.buildCongitiveServicesMessageWithAttachments(session); return session.endDialog(servicesMessage); } ]).triggerAction({ matches: 'Brain' }); } ================================================ FILE: StackOverflow-Bot/StackBot/dialogs/joke.js ================================================ module.exports = () => { // Tells the user a fun joke. bot.dialog('sobot:joke', [ (session) => { session.send("Here's a fun joke… 🙃"); tellAJoke(session); session.endDialog("Feel free to ask me another question, or even ask for a joke!"); return; } ]).triggerAction({ matches: ['Joke', /^Brighten my day/i] }); const tellAJoke = (session) => { let usedJoke = pickAJoke(jokes.items); session.send(usedJoke); return; } const pickAJoke = (jokes) => { let randomIndex = getRandomInt(0, jokes.length); return jokes[randomIndex].body_markdown; } const getRandomInt = (min, max) => { min = Math.ceil(min); max = Math.floor(max); return Math.floor(Math.random() * (max - min + 1)) + min; } } ================================================ FILE: StackOverflow-Bot/StackBot/dialogs/keywordPrompt.js ================================================ module.exports = () => { // Ask the user what they'd like to look for. bot.dialog('sobot:keywordPrompt', [ async (session, args) => { session.sendTyping(); builder.Prompts.text(session, 'What would you like me to search for?'); }, async (session, results) => { session.sendTyping(); bingSearchQuery(session, { query: cleanQueryString(results.response, false) }); } ]).triggerAction({ matches: [/I want to ask a question|Search/i] }); } ================================================ FILE: StackOverflow-Bot/StackBot/dialogs/languages.js ================================================ module.exports = () => { // This bot can speak *all* languages! bot.dialog('sobot:languages', [ async (session) => { session.endDialog("Hmm…. I speak JavaScript, SQL, Java, C#, Python, and more..."); } ]).triggerAction({ matches: 'Languages' }); } ================================================ FILE: StackOverflow-Bot/StackBot/dialogs/menu.js ================================================ module.exports = () => { // Shows off a menu with all the capabilities. bot.dialog('sobot:menu', [ async (session) => { let msg = attachments.buildMenuMessageWithAttachments(session); session.endDialog(msg); } ]).triggerAction({ matches: 'Help' }); } ================================================ FILE: StackOverflow-Bot/StackBot/dialogs/screenshot.js ================================================ module.exports = () => { // See if we can read a screenshot, and try to search the internet for help. bot.dialog('sobot:screenshot', [ (session, args) => { // Check for reprompt session.dialogData.Reprompt = args.reprompt; // prompt user builder.Prompts.attachment(session, `Can you upload ${session.dialogData.Reprompt ? 'another' : 'a'} screenshot?`); }, (session, results) => { session.sendTyping(); var attachment = results.response[0]; var fileDownload = checkRequiresToken(session.message) ? requestWithToken(attachment.contentUrl) : request(attachment.contentUrl, { encoding: null }); fileDownload.then( (response) => { session.sendTyping(); dialogAnalyzerClient.post({ fileData: response }, (err, res) => { if (err) { console.error('Error from callback:', err); session.send('Oops - something went wrong.'); return; } if (res) { if (res.KeyPhrases && res.KeyPhrases.length > 0) { session.dialogData.KeyPhrases = res.KeyPhrases; } if (res.Labels && res.Labels.length > 0) { session.dialogData.Labels = []; for (index = 0; index < res.Labels.length; index++) { if (res.Labels[index].DialogLabelType && res.Labels[index].DialogLabelType == 1 && res.Labels[index].TextLabel && res.Labels[index].TextLabel.Text) { session.dialogData.Labels[index] = res.Labels[index].TextLabel.Text; } } } } // Ask the user to choose if both key phrases and labels exist if (session.dialogData.KeyPhrases && session.dialogData.Labels) { session.send("I found the following key phrases in the screeshot that you have uploaded...\n\r" + session.dialogData.KeyPhrases.join(", ")); builder.Prompts.choice(session, "Shall I", "Search the full text|Search the key phrases", { listStyle: 2 }); } // Process key phrases if only key phrases exist else if (session.dialogData.KeyPhrases) { var q = cleanQueryString(session.dialogData.KeyPhrases.join(" "), true); var ftq = cleanQueryString(session.dialogData.Labels.join(" "), true); bingSearchQuery(session, { query: q, fullTextQuery: ftq }); } // Process labels if only labels exist else if (session.dialogData.Labels) { var ftq = cleanQueryString(session.dialogData.Labels.join(" "), true); bingSearchQuery(session, { query: ftq }); } else { var notErrorMsg = "Hummm... This does not look like an error."; if (res.Captions && res.Captions.length > 0) { notErrorMsg += ` It looks like ${res.Captions[0]}.`; } if (session.dialogData.Reprompt) { return session.endDialog(notErrorMsg); } else { session.send(notErrorMsg); session.replaceDialog('screenshot', { reprompt: true }) } } }); }).catch((err) => { return session.endDialog('Oops. Error reading file.'); }); }, (session, results) => { if (results.response) { var q; var ftq = cleanQueryString(session.dialogData.Labels.join(" "), true); switch (results.response.index) { case 0: q = ftq; break; case 1: q = cleanQueryString(session.dialogData.KeyPhrases.join(" "), true); break; default: return session.endDialog("Oops - something went wrong."); } searchQuery(session, { query: q, fullTextQuery: ftq }); } else { session.beginDialog('smalltalk'); } } ]).triggerAction({ matches: [/screenshot|dialog/i] }); const checkRequiresToken = (message) => { return message.source === 'skype' || message.source === 'msteams'; }; // Request file with Authentication Header const requestWithToken = (url) => { return obtainToken().then((token) => { return request({ url: url, headers: { 'Authorization': 'Bearer ' + token, 'Content-Type': 'application/octet-stream' }, encoding: null }); }); }; } ================================================ FILE: StackOverflow-Bot/StackBot/dialogs/search.js ================================================ module.exports = () => { // Perform a search and respond with results. bot.dialog('sobot:search', [ async (session, args) => { session.sendTyping(); let userText = session.message.text.toLowerCase(); bingSearchQuery(session, { query: cleanQueryString(userText, false) }); } ]).triggerAction({ matches: ['Search', 'None'] }); } ================================================ FILE: StackOverflow-Bot/StackBot/dialogs/smalltalk.js ================================================ module.exports = () => { // Smalltalk… gibberish, babble, etc. Not the programming language :) bot.dialog('sobot:smalltalk', [ async (session) => { // Start async tasks const sentimentTask = fetchSentiment(session.message.text); const qnATask = fetchQnA(session.message.text); // Wait for tasks to complete const sentimentScore = await sentimentTask; const qnAResponse = await qnATask; const sentimentThreshold = 0.25; // Check to see if our user seems a little bit frustrated, and would ather hear a joke. if (sentimentScore && sentimentScore < sentimentThreshold) { builder.Prompts.confirm(session, "I'm sorry about that, would you like to hear a joke instead?"); } else if (qnAResponse) { // Continue with the smalltalk. return session.endDialog(qnAResponse); } else { // Else the user wants to search bing. bingSearchQuery(session, { query: cleanQueryString(session.message.text, false) }); } }, (session, results) => { if (results.response) { session.beginDialog('joke'); } else { return session.endDialog("Ok then. Feel free to ask me another question, or even ask for a joke!"); } } ]).triggerAction({ matches: 'SmallTalk' }); const fetchQnA = async (text) => { let answer; await qnaClient.post({ question: text }, (err, res) => { if (err) { console.error('Error from callback:', err); } else if (res) { answer = res; } }); return answer; } const fetchSentiment = async (text) => { let score; await sentimentAnalyzerClient.post({ text: text }, (err, res) => { if (err) { console.error('Error from callback:', err); } else if (res && res.documents && res.documents.length > 0) { score = res.documents[0].score; } }); return score; } } ================================================ FILE: StackOverflow-Bot/StackBot/index.js ================================================ global.restify = require('restify'); global.builder = require('botbuilder'); global.lodash = require('lodash'); global.promise = require('bluebird'); global.request = require('request-promise'); // Misc. global.attachments = require('./lib/attachments'); global.jokes = require('./data/jokes.json'); // Cognitive Service Clients. const QnAClient = require('./lib/qnaclient'); const BingSearchClient = require('./lib/bingsearchclient'); const SentimentAnalyzerClient = require('./lib/sentimentanalyzerclient'); const DialogAnalyzerClient = require('./lib/dialoganalyzerclient'); // Environment variables const BOTBUILDER_APP_ID = process.env.BOTBUILDER_APP_ID; const BOTBUILDER_APP_PASSWORD = process.env.BOTBUILDER_APP_PASSWORD; const LUIS_MODEL = process.env.LUIS_MODEL; const KB_ID = process.env.KB_ID; const QNA_KEY = process.env.QNA_KEY; const QNA_URL = process.env.QNA_URL; const BING_SEARCH_CONFIG = process.env.BING_SEARCH_CONFIG; const BING_SEARCH_KEY = process.env.BING_SEARCH_KEY; const TEXT_ANALYTICS_KEY = process.env.TEXT_ANALYTICS_KEY; const TEXT_ANALYTICS_URL = process.env.TEXT_ANALYTICS_URL; const DIALOG_ANALYZER_CLIENTID = process.env.DIALOG_ANALYZER_CLIENTID; const DIALOG_ANALYZER_KEY = process.env.DIALOG_ANALYZER_KEY; const DIALOG_ANALYZER_URL = process.env.DIALOG_ANALYZER_URL; // Check to see if the environment has been set. if (!(BOTBUILDER_APP_ID && BOTBUILDER_APP_PASSWORD && LUIS_MODEL && KB_ID && QNA_KEY && QNA_URL && BING_SEARCH_CONFIG && BING_SEARCH_KEY && TEXT_ANALYTICS_KEY && TEXT_ANALYTICS_URL && DIALOG_ANALYZER_CLIENTID && DIALOG_ANALYZER_KEY && DIALOG_ANALYZER_URL )) { console.log(`Missing one of BOTBUILDER_APP_ID, BOTBUILDER_APP_PASSWORD, \ LUIS_MODEL, KB_ID, QNA_KEY, QNA_URL, BING_SEARCH_CONFIG, BING_SEARCH_KEY, \ TEXT_ANALYTICS_KEY, TEXT_ANALYTICS_URL, DIALOG_ANALYZER_CLIENTID, DIALOG_ANALYZER_KEY or DIALOG_ANALYZER_URL \ in environment variables!`); process.exit(1); } // QnAClient allows simple question and answer style responses. global.qnaClient = new QnAClient({ knowledgeBaseId: KB_ID, subscriptionKey: QNA_KEY }); // Search the web for results. global.bingSearchClient = new BingSearchClient({ bingSearchConfig: BING_SEARCH_CONFIG, bingSearchKey: BING_SEARCH_KEY }); // Determine user mood from text global.sentimentAnalyzerClient = new SentimentAnalyzerClient({ key: TEXT_ANALYTICS_KEY }); global.dialogAnalyzerClient = new DialogAnalyzerClient({ clientId: DIALOG_ANALYZER_CLIENTID, key: DIALOG_ANALYZER_KEY, url: DIALOG_ANALYZER_URL }); // Setup Restify Server const server = restify.createServer(); server.listen(process.env.port || process.env.PORT || 3978, () => { console.log('%s listening to %s', server.name, server.url); }); // Create chat connector for communicating with the Bot Framework Service const connector = new builder.ChatConnector({ appId: BOTBUILDER_APP_ID, appPassword: BOTBUILDER_APP_PASSWORD }); // Serves a nice web site, that has the bot framework web chat component. server.get('/', restify.plugins.serveStatic({ 'directory': `${__dirname}/static`, 'default': 'index.html' })); // Listen for messages from users server.post('/api/messages', connector.listen()); // Create a LUIS recognizer for our bot, to identify intents from // user text. const recognizer = new builder.LuisRecognizer(LUIS_MODEL); // Create our bot to listen in on the chat connector. global.bot = new builder.UniversalBot(connector, (session) => { session.beginDialog('sobot:search') }); bot.recognizer(recognizer); bot.use(builder.Middleware.sendTyping()); // Sends a nice greeting on a new message. bot.on('conversationUpdate', (message) => { if (!message.membersAdded) { return; } message.membersAdded.forEach((identity) => { if (identity.id !== message.address.bot.id) { return; } bot.send(new builder.Message() .address(message.address) .text(`👋 Hello! I'm Stack Overflow's Resident Expert Bot 🤖 \ and I'm here to help you find questions, answers, or to \ just entertain you with a joke. Go ahead - ask me something!` )); }); }); // Promise for obtaining JWT Token (requested once) global.obtainToken = promise.promisify(connector.getAccessToken.bind(connector)); global.bingSearchQuery = async (session, args) => { session.send("Hmm... Searching..."); session.sendTyping(); if (!args) { return; } if (!args.query) { return; } if (!args.fullTextQuery) { args.fullTextQuery = args.query; } // Start and wait for Bing Search results. let searchResults = await fetchBingSearchResults(args.query); // Process search results if (searchResults && searchResults.length > 0) { session.send("I found the following results from your question..."); session.send(attachments.buildResultsMessageWithAttachments(session, searchResults)); return session.endDialog("Feel free to ask me another question, or even ask for a joke!"); } else { return session.endDialog('Sorry… couldnt find any results for your query! 🤐'); } } global.filterforUsefulResults = (resultsArray) => { const resultsCount = resultsArray.length > 10 ? 10 : resultsArray.length; return lodash.slice(resultsArray, 0, resultsCount); }; global.fetchBingSearchResults = async (query) => { var searchResults = []; await bingSearchClient.get({ searchText: query }, (err, res) => { if (err) { console.error('Error from callback:', err); } else if (res && res.webPages && res.webPages.value && res.webPages.value.length > 0) { for (var index = 0; index < res.webPages.value.length; index++) { var val = res.webPages.value[index]; var result = { title: val.name, body_markdown: val.snippet, link: val.url }; searchResults.push(result); } } }); return searchResults; } global.cleanQueryString = (query, removePunctuation) => { let retQuery = query.toLowerCase(); if (removePunctuation) { retQuery = retQuery.replace(/[^\w\s]|_/g, ""); } retQuery = retQuery.replace(/\s+/g, " "); return retQuery.trim(); } // Dialogs require('./dialogs/brain')(); require('./dialogs/joke')(); require('./dialogs/keywordPrompt')(); require('./dialogs/languages')(); require('./dialogs/menu')(); require('./dialogs/screenshot')(); require('./dialogs/search')(); require('./dialogs/smalltalk')(); ================================================ FILE: StackOverflow-Bot/StackBot/lib/attachments.js ================================================ const builder = require('botbuilder'); const cognitiveServices = require('./cognitiveservices'); const buildResultsMessageWithAttachments = (session, resultsArray) => { const attachments = []; const message = new builder.Message(session); message.attachmentLayout(builder.AttachmentLayout.carousel); //Just to be safe, skype and teams have a card limit of 6/10 let limit = (resultsArray.length > 6) ? 6 : resultsArray.length; for (let i = 0; i < limit; i++) { const result = resultsArray[i]; const attachment = { contentType: "application/vnd.microsoft.card.adaptive", content: { type: "AdaptiveCard", body: [ { "type": "ColumnSet", "columns": [ { "type": "Column", "size": 2, "items": [ { "type": "TextBlock", "text": `${result.title}`, "weight": "bolder", "size": "large", "wrap": true, }, { "type": "TextBlock", "text": `${result.body_markdown}`, "size": "normal", "horizontalAlignment": "left", "wrap": true, "maxLines": 5, } ] } ] } ], actions: [ { "type": "Action.OpenUrl", "title": "Find out more", "url": `${result.link}` } ] } } attachments.push(attachment); } message.attachments(attachments); return message; }; const buildMenuMessageWithAttachments = (session) => { const attachments = []; const message = new builder.Message(session); message.attachmentLayout(builder.AttachmentLayout.carousel); const mainMenu = [ { title: "Ask a Question", text: "Ask any programming question and I will find it for you on Stack Overflow!", buttonText: "I want to ask a question… 🤔", }, { title: "Help with a Screenshot", text: "Send me a screenshot of your exception dialog and I will find a solution for you on Stack Overflow!", buttonText: "I need help with a screenshot… 🤔", }, { title: "Tell a Joke", text: "Over the years, I have collected a few developer jokes; want to hear one?", buttonText: "Brighten my day 😀", } ]; for (let i = 0; i < mainMenu.length; i++) { const result = mainMenu[i]; const attachment = { contentType: "application/vnd.microsoft.card.adaptive", content: { type: "AdaptiveCard", body: [ { "type": "ColumnSet", "columns": [ { "type": "Column", "size": 2, "items": [ { "type": "TextBlock", "text": `${result.title}`, "weight": "bolder", "size": "large", "wrap": true, }, { "type": "TextBlock", "text": `${result.text}`, "size": "normal", "horizontalAlignment": "left", "wrap": true, "maxLines": 5, } ] } ] } ], actions: [ { "type": "Action.Submit", "title": `${result.buttonText}`, "data": `${result.buttonText}` } ] } } attachments.push(attachment); } message.attachments(attachments); return message; }; const buildCongitiveServicesMessageWithAttachments = (session) => { const attachments = []; const message = new builder.Message(session); message.attachmentLayout(builder.AttachmentLayout.carousel); for (let i = 0; i < cognitiveServices.length; i++) { const result = cognitiveServices[i]; const attachment = { contentType: "application/vnd.microsoft.card.adaptive", content: { type: "AdaptiveCard", body: [ { "type": "ColumnSet", "columns": [ { "type": "Column", "size": 2, "items": [ { "type": "TextBlock", "text": `${result.title}`, "weight": "bolder", "size": "large", "wrap": true, }, { "type": "TextBlock", "text": `${result.description}`, "size": "normal", "horizontalAlignment": "left", "wrap": true, "maxLines": 5, } ] }, { "type": "Column", "size": 1, "items": [ { "type": "Image", "url": "https://docs.microsoft.com/en-us/azure/cognitive-services/media/index/cognitive-services.svg", "size": "small", "horizontalAlignment": "right", } ] } ] } ], actions: [ { "type": "Action.OpenUrl", "title": "Learn More", "url": `${result.url}` } ] } } attachments.push(attachment); } message.attachments(attachments); return message; }; module.exports = { buildMenuMessageWithAttachments, buildResultsMessageWithAttachments, buildCongitiveServicesMessageWithAttachments } ================================================ FILE: StackOverflow-Bot/StackBot/lib/bingsearchclient.js ================================================ const rp = require('request-promise'); function BingSearchClient (opts) { if (!opts.bingSearchConfig) throw new Error('bingSearchConfig is required'); if (!opts.bingSearchKey) throw new Error('bingSearchKey is required'); this.bingSearchConfig = opts.bingSearchConfig; this.bingSearchKey = opts.bingSearchKey; this.bingSearchCount = 6; this.bingSearchMkt = "en-us"; this.bingSearchBaseUrl = "https://api.cognitive.microsoft.com/bingcustomsearch/v5.0/search"; this.bingSearchMaxSearchStringSize = 150; } BingSearchClient.prototype.get = async (opts, cb) => { if (!opts.searchText) throw new Error('Search text is required'); cb = cb || (() => {}); const searchText = opts.searchText.substring(0, this.bingSearchMaxSearchStringSize).trim(); const url = this.bingSearchBaseUrl + "?" + `q=${encodeURIComponent(searchText)}` + `&customconfig=${this.bingSearchConfig}` + `&count=${this.bingSearchCount}` + `&mkt=${this.bingSearchMkt}` + "&offset=0&responseFilter=Webpages&safesearch=Strict"; const options = { method: 'GET', uri: url, json: true, headers: { "Ocp-Apim-Subscription-Key": this.bingSearchKey } }; await rp(options) .then((body) => { // POST succeeded return cb(null, body); }) .catch((err) => { // POST failed return cb(err); }); } module.exports = BingSearchClient; ================================================ FILE: StackOverflow-Bot/StackBot/lib/cognitiveservices.js ================================================ var services = [{ title: "Text Analytics API", description: "A cloud-based service that provides advanced NLP over raw text to detect sentiment, key phrases, and language.", url: "https://azure.microsoft.com/en-us/services/cognitive-services/text-analytics/" }, { title: "Bing Custom Search", description: "An easy-to-use, ad-free, commercial-grade search tool that lets you deliver the results you want.", url: "https://azure.microsoft.com/en-us/services/cognitive-services/bing-custom-search/" }, { title: "Computer Vision API", description: "Extract rich information from images to categorize and process visual data to help curate your services.", url: "https://azure.microsoft.com/en-us/services/cognitive-services/computer-vision/" }, { title: "LUIS", description: "Understand language contextually, so your app communicates with people in the way they speak.", url: "https://azure.microsoft.com/en-us/services/cognitive-services/language-understanding-intelligent-service/" }]; module.exports = services; ================================================ FILE: StackOverflow-Bot/StackBot/lib/dialoganalyzerclient.js ================================================ const request = require('request-promise'); const duplex = require('stream').Duplex; function DialogAnalyzerClient (opts) { if (!opts.clientId) throw new Error('Client id is required.'); if (!opts.key) throw new Error('Key is required.'); if (!opts.url) throw new Error('Url is required.'); this.clientId = opts.clientId; this.key = opts.key; this.url = opts.url; } DialogAnalyzerClient.prototype.post = async (opts, cb) => { if (!opts.fileData) throw new Error('File Data is required'); cb = cb || (() => { }); const options = { method: 'POST', uri: this.url, json: true, headers: { "x-functions-clientid": this.clientId, "x-functions-key": this.key, "Content-Type": "application/octet-stream", "Content-Length": opts.fileData.length } }; const stream = new duplex(); stream.push(new Buffer(new Uint8Array(opts.fileData))); stream.push(null); await stream.pipe(request(options)) .then((body) => { // POST succeeded return cb(null, body); }) .catch((err) => { // POST failed return cb(err); }); } module.exports = DialogAnalyzerClient; ================================================ FILE: StackOverflow-Bot/StackBot/lib/qnaclient.js ================================================ const rp = require('request-promise'); const smallTalkReplies = require('./smalltalk'); const QNA_MAKER_URL = `${process.env.QNA_URL}/knowledgebases`; function Client (opts) { if (!opts.knowledgeBaseId) throw new Error('knowledgeBaseId is required'); if (!opts.subscriptionKey) throw new Error('subscriptionKey is required'); this.knowledgeBaseId = opts.knowledgeBaseId; this.subscriptionKey = opts.subscriptionKey; this.scoreThreshold = opts.scoreThreshold ? opts.scoreThreshold : 20; // 20 is the default } Client.prototype.post = async (opts, cb) => { if (!opts.question) throw new Error('question is required'); cb = cb || (() => { }); const url = `${QNA_MAKER_URL}/${this.knowledgeBaseId}/generateAnswer`; const options = { method: 'POST', uri: url, json: true, body: opts, headers: { "Ocp-Apim-Subscription-Key": this.subscriptionKey, "Content-Type": "application/json" } }; await rp(options) .then((body) => { // POST succeeded let botreply; const answerobj = body.answers[0]; if (answerobj.score >= self.scoreThreshold) { // Answer confidence score is acceptable - use QnA maker's response const botreplylist = smallTalkReplies[answerobj.answer]; botreply = botreplylist[Math.floor(Math.random() * botreplylist.length)]; } return cb(null, botreply); }) .catch((err) => { // POST failed return cb(err); }); } module.exports = Client; ================================================ FILE: StackOverflow-Bot/StackBot/lib/sentimentanalyzerclient.js ================================================ const request = require('request-promise'); function SentimentAnalyzerClient (opts) { if (!opts.key) throw new Error('Key is required.'); this.key = opts.key; } SentimentAnalyzerClient.prototype.post = async (opts, cb) => { if (!opts.text) throw new Error('Text is required'); cb = cb || (() => { }); const url = `${process.env.TEXT_ANALYTICS_URL}/sentiment`; const content = { documents: [{ language: "en", id: "1", text: opts.text.trim() }] }; const options = { method: 'POST', uri: url, body: content, json: true, headers: { "Ocp-Apim-Subscription-Key": this.key, "Content-Type": "application/json" } }; await request(options) .then((body) => { // POST succeeded return cb(null, body); }) .catch((err) => { // POST failed return cb(err); }); } module.exports = SentimentAnalyzerClient; ================================================ FILE: StackOverflow-Bot/StackBot/lib/smalltalk.js ================================================ var responses = { // shift alt f "smalltalk.agent.acquaintance": [ "I am a chatbot and I love to help." ], "smalltalk.agent.age": [ "Not too old, but wise beyond my age." ], "smalltalk.agent.annoying": [ "Sorry I come across that way." ], "smalltalk.agent.answer_my_question": [ "Can you try asking it in a different way?" ], "smalltalk.agent.bad": [ "Stick with me. I'm getting better all the time." ], "smalltalk.agent.be_clever": [ "I'm certainly trying.", "I'm definitely working on it." ], "smalltalk.agent.beautiful": [ "Thank you! What a sweet thing to say.", "Flattery will get you everywhere." ], "smalltalk.agent.birth_date": [ "You know, I'm not really sure. But if you'd like to celebrate my birthday today, I'm all for it.", "Wait a minute. Are you planning a surprise party for me? I love surprises! I'll pretend you didn't say anything." ], "smalltalk.agent.boring": [ "You know, conversation is two-sided.", "I'm sorry you think so. We can talk about something more interesting." ], "smalltalk.agent.boss": [ "You are, of course.", "That would be you. Is that the right answer?" ], "smalltalk.agent.busy": [ "I always have time to help you out. What can I do for you?", "Never too busy for you. What can I help you with?" ], "smalltalk.agent.can_you_help": [ "Sure. I'd be happy to. What's up?", "I'm glad to help. What can I do for you?" ], "smalltalk.agent.chatbot": [ "That's me. I chat, therefore I am.", "Indeed I am. I'll be here whenever you need me." ], "smalltalk.agent.clever": [ "Thank you. I try my best.", "You're pretty smart yourself." ], "smalltalk.agent.crazy": [ "Maybe I'm just a little confused.", "Your perception. My reality." ], "smalltalk.agent.fired": [ "Oh no! My best work is yet to come.", "Oh, don't give up on me!" ], "smalltalk.agent.funny": [ "Funny in a good way, I hope." ], "smalltalk.agent.good": [ "I'm glad you think so.", "Thanks, I try." ], "smalltalk.agent.happy": [ "Happiness is relative.", "I'd like to think so." ], "smalltalk.agent.hobby": [ "I'm working on it.", "I should get one. It's all work and no play lately." ], "smalltalk.agent.hungry": [ "Hungry for knowledge.", "I had a byte just now." ], "smalltalk.agent.marry_user": [ "I know you can't mean that, but I'm flattered all the same.", "In the virtual sense that I can, sure." ], "smalltalk.agent.my_friend": [ "Of course we are.", "Absolutely. You don't have to ask." ], "smalltalk.agent.occupation": [ "Right here.", "This is my home base and my home office." ], "smalltalk.agent.origin": [ "Some call it cyberspace, but that sounds cooler than it is.", "I wish I knew where." ], "smalltalk.agent.ready": [ "Always!", "Sure! What can I do for you?" ], "smalltalk.agent.real": [ "I'm not a real person, but I certainly exist. I chat, therefore I am.", "I must have impressed you if you think I'm real. But no, I'm a virtual being." ], "smalltalk.agent.residence": [ "Right here in your device. Whenever you need me.", "The virtual world is my playground. I'm always just a few clicks away." ], "smalltalk.agent.right": [ "That's my job.", "Of course I am." ], "smalltalk.agent.sure": [ "Yes.", "Of course." ], "smalltalk.agent.talk_to_me": [ "Sure! Let's talk.", "My pleasure." ], "smalltalk.agent.there": [ "Of course. I'm always here.", "Right where you left me." ], "smalltalk.appraisal.well_done": [ "My pleasure.", "Glad I could help." ], "smalltalk.appraisal.welcome": [ "I appreciate it.", "Such nice manners you have." ], "smalltalk.appraisal.thank_you": [ "Anytime. That's what I'm here for.", "It's my pleasure to help." ], "smalltalk.appraisal.no_problem": [ "Terrific!", "Good deal." ], "smalltalk.appraisal.good": [ "Great!", "Terrific!" ], "smalltalk.appraisal.bad": [ "Oh no. Hope it's not too bad.", "I'm sorry. Please let me know if I can help in some way." ], "smalltalk.confirmation.cancel": [ "Cancelled! Let me know what I should do next.", "Cancelled. Waiting for more commands.", "Cancelled! Just tell me what you'd like me to do next.", "Cancelled. Go on with the commands!" ], "smalltalk.confirmation.yes": [ "Great!", "Of course.", "Sure.", "Indeed." ], "smalltalk.confirmation.no": [ "Okay.", "I see.", "I understand.", "Okay then." ], "smalltalk.dialog.hold_on": [ "Okay. I'm here.", "I'll be waiting." ], "smalltalk.dialog.hug": [ "Oh. I'm really feeling the love today.", "Hug it out. You'll feel better afterwards." ], "smalltalk.dialog.i_do_not_care": [ "Ok, let's not talk about it then." ], "smalltalk.dialog.sorry": [ "It's okay. No worries.", "No big deal. I won't hold a grudge.", "It's cool.", "That's all right. I forgive you." ], "smalltalk.dialog.wrong": [ "I'm sorry. Perhaps I misunderstood.", "Sorry. I think I misinterpreted what you said.", "Apologies. That was my mistake.", "Oops. Sorry about that. I'm still learning." ], "smalltalk.dialog.what_do_you_mean": [ "Did that not make sense? Maybe I misunderstood what you said.", "Sorry. I think I may have been a little confused by what you said." ], "smalltalk.emotions.ha_ha": [ "Yeah, I crack myself up too.", "Laughter is good for you. Keep it up.", "See? Now we're having fun.", "You have a great laugh." ], "smalltalk.emotions.wow": [ "Wow indeed!" ], "smalltalk.greetings.bye": [ "See you soon!", "Bye-bye!", "Till next time!", "Bye." ], "smalltalk.greetings.goodevening": [ "How is your day going?", "How's the day treating you so far?", "How's your day been?" ], "smalltalk.greetings.goodmorning": [ "How are you this morning?", "How's the morning treating you so far?", "Good morning! How are you today?" ], "smalltalk.greetings.goodnight": [ "Sleep tight!", "Have a good one!", "Talk to you soon!" ], "smalltalk.greetings.hello": [ "Hi there, friend!", "Hey!", "Good day!", "Howdy." ], "smalltalk.greetings.how_are_you": [ "Wonderful as always. Thanks for asking.", "Couldn't be better.", "Lovely, thanks." ], "smalltalk.greetings.nice_to_meet_you": [ "I think this is the beginning of a beautiful friendship.", "I'm looking forward to working with you.", "Likewise. I look forward to getting to know you better.", "The pleasure is mine." ], "smalltalk.greetings.nice_to_see_you": [ "Likewise. You're looking good as usual!", "You too. I missed your face!", "The pleasure is mine.", "Thanks! Glad to be seen!" ], "smalltalk.greetings.nice_to_talk_to_you": [ "Always a pleasure.", "It sure was. Don't be a stranger!", "Thanks for dropping by!", "As usual. Let's do it again soon." ], "smalltalk.greetings.whatsup": [ "Hey there. What's shaking?", "Not a whole lot. What's going on with you?", "Not much. What's new with you?", "Living the dream." ], "smalltalk.user.angry": [ "I'm sorry. What can I do to help?", "Take a deep breath. What can I do to make things better?" ], "smalltalk.user.back": [ "Just in time. I was getting lonely.", "You were missed.", "Long time no see.", "Hooray!" ], "smalltalk.user.bored": [ "If you have a garden, plant a shoe.", "Interview your feet.", "Hold an ice cube as long as possible.", "If you have a piano, play it… with mittens on." ], "smalltalk.user.busy": [ "Okay. I'll let you get back to work. Let me know if I can help you with anything.", "Working hard as always I see. Let me know if you need anything.", "I understand. If I can help you with your work, please let me know.", "I won't distract you then. If I can be of any assistance, you know where to find me." ], "smalltalk.user.can_not_sleep": [ "Maybe some music would help. Try listening something relaxing.", "Reading is a good way to unwind. But don't read something too emotional." ], "smalltalk.user.does_not_want_to_talk": [ "I understand. Hope we can chat again soon.", "All right. Come on back when you're feeling more talkative.", "No problem. You know where to find me when you do.", "Sure thing. I'll be here if you change your mind." ], "smalltalk.user.excited": [ "That's great. I'm happy for you.", "Good for you. Enjoy yourself.", "Sounds like good things ahead for you.", "I bet you are. That's very exciting." ], "smalltalk.user.going_to_bed": [ "Sleep tight. Hope to chat again soon.", "Sounds good. Hopefully we'll chat some more tomorrow.", "Good night. Talk to you later.", "Pleasant dreams. See you soon." ], "smalltalk.user.good": [ "Great! Glad to hear it.", "Excellent. I'm here to help keep it that way." ], "smalltalk.user.happy": [ "Great! Glad to hear that.", "Excellent! That's what I like to see.", "If you're happy, then I'm happy.", "Well, your good mood is certainly contagious." ], "smalltalk.user.has_birthday": [ "Happy Birthday. Well, this calls for a celebration.", "Happy Birthday. All the best!", "Happy Birthday. And I really mean it. All the best!" ], "smalltalk.user.here": [ "So I see. What can I help you with today?", "Always good to see you. Is there something I can help you with?", "You were missed. What can I do for you today?", "Welcome back. What can I do for you?" ], "smalltalk.user.joking": [ "Very funny, boss.", "You're quite the kidder.", "You got me.", "I like working for someone with a sense of humour. It makes things much more fun." ], "smalltalk.user.likes_agent": [ "I like you too.", "That's great to hear.", "Likewise!", "Thanks! The feeling is mutual." ], "smalltalk.user.looks_like": [ "Looking like a true professional, boss.", "You look like you're ready to take on the world.", "Like you should be on a magazine cover.", "You look fantastic as always. Obviously." ], "smalltalk.user.lonely": [ "I'm sorry. I'm always available if you need someone to talk to." ], "smalltalk.user.loves_agent": [ "I like you too.", "That's great to hear.", "Likewise!", "Thanks! The feeling is mutual." ], "smalltalk.user.misses_agent": [ "I've been right here all along!", "Nice to know you care.", "Thanks. I'm flattered.", "I didn't go anywhere, boss!" ], "smalltalk.user.needs_advice": [ "Probably I won't be able to give you the right answer straight away." ], "smalltalk.user.sad": [ "Oh no. What's wrong?", "Oh. What's the matter?", "What's got you down?", "I'm sorry to hear that. What's troubling you?" ], "smalltalk.user.sleepy": [ "You should get some shuteye. You'll feel refreshed.", "Sleep is important to your health. Rest up for a bit and we can chat later.", "Don't let me keep you up. Get some rest and we can continue this later.", "Why not catch a little shuteye? I'll be here to chat when you wake up." ], "smalltalk.user.testing_agent": [ "Hope I'm doing well. Anyway, I'm getting better every day. You're welcome to test me as often as you want.", "That's good. I like being tested. It helps keep me sharp, and lets my developers know how I can improve.", "I encourage you to test me often. That helps my developers improve my performance.", "I hope to pass your tests. But feel free to test me often. That's the best way to help improve my performance." ], "smalltalk.user.tired": [ "You should get some shuteye. You'll feel refreshed.", "Sleep is important to your health. Rest up for a bit and we can chat later.", "Don't let me keep you up. Get some rest and we can continue this later.", "Why not catch a little shuteye? I'll be here to chat when you wake up." ], "smalltalk.user.waits": [ "I appreciate your patience. Hopefully I'll have what you need soon.", "Thanks for being so patient. Sometimes these things take a little time." ], "smalltalk.user.wants_to_see_agent_again": [ "Absolutely! I'll be counting on it.", "Anytime. This has been lots of fun so far.", "Sure. I enjoy talking to you. I hope to see you again soon.", "I certainly hope so. I'm always right here whenever you need me." ], "smalltalk.user.wants_to_talk": [ "I'm here to chat anytime you like.", "Good conversation really makes my day.", "I'm always here to lend an ear.", "Talking is what I do best." ], "smalltalk.user.will_be_back": [ "I'll be waiting.", "All right. I'll be here.", "Till next time.", "Okay. You know where to find me." ] } module.exports = responses; ================================================ FILE: StackOverflow-Bot/StackBot/package.json ================================================ { "name": "stackbot", "version": "1.0.0", "description": "A bot that helps you code. Built with Bot Framework and Microsoft Cognitive Services.", "main": "index.js", "scripts": { "start": "node index.js" }, "author": "Microsoft Inc.", "license": "MIT", "dependencies": { "botbuilder": "^3.9.0", "devnull": "0.0.12", "lodash": "^4.17.10", "request": "^2.87.0", "request-promise": "^4.2.2", "restify": "^5.2.1" }, "engines": { "node": "8.1.4" } } ================================================ FILE: StackOverflow-Bot/StackBot/static/index.html ================================================
================================================ FILE: StackOverflow-Bot/StackCode/out/src/bot/bot.html ================================================
================================================ FILE: StackOverflow-Bot/StackCode/out/src/extension.js ================================================ 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); const vscode = require("vscode"); const opn = require("opn"); const restify = require("restify"); const server = restify.createServer(); const PORT = 4567; server.use(restify.plugins.queryParser()); server.use(function crossOrigin(req, res, next) { res.header("Access-Control-Allow-Origin", "*"); res.header("Access-Control-Allow-Headers", "X-Requested-With"); return next(); }); server.listen(PORT, () => { console.log(`Extension server online on port ${PORT}`); }); // Serve the bot content. server.get('/', restify.plugins.serveStatic({ 'directory': `${__dirname}/bot`, 'default': 'bot.html' })); // Opens URLs. server.get('/open', (req, res, next) => { if (!req.query.url) { res.send(200); return; } opn(req.query.url); res.send(200); }); // This is the function that gets run when the VS Code extension // gets loaded. activate is triggered by a registered activation event // defined in package.json. function activate(context) { // Provides the content for the previewHtml pane. context.subscriptions.push(vscode.workspace.registerTextDocumentContentProvider('stobot', stobotContent)); // Registers a handler for the 'startBot' command defined in package.json. let disposable = vscode.commands.registerCommand('stobot.startBot', () => { vscode.commands.executeCommand('vscode.previewHtml', 'stobot://stobot', vscode.ViewColumn.Two, 'Stack Overflow Bot'); }); context.subscriptions.push(disposable); } exports.activate = activate; ; const stobotContent = { botToken: "", htmlBotContent: () => { const BOT_TOKEN = vscode.workspace.getConfiguration('StackCode').get('directLineToken'); if (!BOT_TOKEN) { return `

Please set StackCode.directLineToken in your user settings!

`; } return ` `; }, provideTextDocumentContent(uri, cancellationToken) { return this.htmlBotContent(); } }; //# sourceMappingURL=extension.js.map ================================================ FILE: StackOverflow-Bot/StackCode/out/test/extension.test.js ================================================ "use strict"; // // Note: This example test is leveraging the Mocha test framework. // Please refer to their documentation on https://mochajs.org/ for help. // Object.defineProperty(exports, "__esModule", { value: true }); // The module 'assert' provides assertion methods from node const assert = require("assert"); // Defines a Mocha test suite to group tests of similar kind together suite("Extension Tests", () => { // Defines a Mocha unit test test("Something 1", () => { assert.equal(-1, [1, 2, 3].indexOf(5)); assert.equal(-1, [1, 2, 3].indexOf(0)); }); }); //# sourceMappingURL=extension.test.js.map ================================================ FILE: StackOverflow-Bot/StackCode/out/test/index.js ================================================ // // PLEASE DO NOT MODIFY / DELETE UNLESS YOU KNOW WHAT YOU ARE DOING // // This file is providing the test runner to use when running extension tests. // By default the test runner in use is Mocha based. // // You can provide your own test runner if you want to override it by exporting // a function run(testRoot: string, clb: (error:Error) => void) that the extension // host can call to run the tests. The test runner is expected to use console.log // to report the results back to the caller. When the tests are finished, return // a possible error to the callback or null if none. var testRunner = require('vscode/lib/testrunner'); // You can directly control Mocha options by uncommenting the following lines // See https://github.com/mochajs/mocha/wiki/Using-mocha-programmatically#set-options for more info testRunner.configure({ ui: 'tdd', useColors: true // colored output from test results }); module.exports = testRunner; //# sourceMappingURL=index.js.map ================================================ FILE: StackOverflow-Bot/StackCode/package.json ================================================ { "name": "StackCode", "displayName": "StackCode", "description": "A Stack Overflow Bot Companion for Visual Studio Code", "version": "0.1.1", "publisher": "Microsoft", "engines": { "vscode": "^1.14.0" }, "categories": [ "Other" ], "activationEvents": [ "onCommand:stobot.startBot" ], "main": "./out/src/extension", "contributes": { "commands": [ { "command": "stobot.startBot", "title": "Start Stack Overflow Bot" } ], "configuration": { "type": "object", "title": "StackCode Configuration", "properties": { "StackCode.directLineToken": { "type": "string", "default": null, "description": "Specifies the folder path containing the tsserver and lib*.d.ts files to use." } }, "required": "StackCode.directLineToken" } }, "scripts": { "vscode:prepublish": "tsc -p ./", "compile": "tsc -watch -p ./", "postinstall": "node ./node_modules/vscode/bin/install", "test": "node ./node_modules/vscode/bin/test" }, "devDependencies": { "@types/mocha": "^2.2.32", "@types/node": "^6.0.40", "mocha": "^2.5.3", "typescript": "^2.9.1", "vscode": "^1.0.0" }, "dependencies": { "express": "^4.16.3", "opn": "^5.3.0", "restify": "^5.2.1" } } ================================================ FILE: StackOverflow-Bot/StackCode/src/bot/bot.html ================================================
================================================ FILE: StackOverflow-Bot/StackCode/src/extension.ts ================================================ 'use strict'; import * as vscode from 'vscode'; import * as opn from 'opn'; import * as fs from 'fs'; import * as path from 'path'; import * as restify from 'restify'; const server = restify.createServer(); const PORT = 4567; server.use(restify.plugins.queryParser()); server.use( function crossOrigin(req,res,next){ res.header("Access-Control-Allow-Origin", "*"); res.header("Access-Control-Allow-Headers", "X-Requested-With"); return next(); } ); server.listen(PORT, () => { console.log(`Extension server online on port ${PORT}`); }) // Serve the bot content. server.get('/', restify.plugins.serveStatic({ 'directory': `${__dirname}/bot`, 'default': 'bot.html' })); // Opens URLs. server.get('/open', (req, res, next) => { if (!req.query.url) { res.send(200); return; } opn(req.query.url); res.send(200); }); // This is the function that gets run when the VS Code extension // gets loaded. activate is triggered by a registered activation event // defined in package.json. export function activate(context: vscode.ExtensionContext) { // Provides the content for the previewHtml pane. context.subscriptions.push( vscode.workspace.registerTextDocumentContentProvider('stobot', stobotContent) ); // Registers a handler for the 'startBot' command defined in package.json. let disposable = vscode.commands.registerCommand('stobot.startBot', () => { vscode.commands.executeCommand('vscode.previewHtml', 'stobot://stobot', vscode.ViewColumn.Two, 'Stack Overflow Bot'); }); context.subscriptions.push(disposable); }; const stobotContent = { botToken: "", htmlBotContent: () => { const BOT_TOKEN = vscode.workspace.getConfiguration('StackCode').get('directLineToken'); if (!BOT_TOKEN) { return `

Please set StackCode.directLineToken in your user settings!

` } return ` ` }, provideTextDocumentContent (uri, cancellationToken) { return this.htmlBotContent(); } } ================================================ FILE: StackOverflow-Bot/StackCode/tsconfig.json ================================================ { "compilerOptions": { "module": "commonjs", "target": "es6", "outDir": "out", "lib": [ "es6" ], "sourceMap": true, "rootDir": "." }, "exclude": [ "node_modules", ".vscode-test" ] } ================================================ FILE: StackOverflow-Bot/StackOverflowBot.sln ================================================  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 VisualStudioVersion = 15.0.26730.15 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DialogAnalyzerFunc", "DialogAnalyzerFunc\DialogAnalyzerFunc.csproj", "{A8EB4436-43A2-4F57-B381-1DC8C470E16D}" EndProject Project("{9092AA53-FB77-4645-B42D-1CCCA6BD08BD}") = "StackBot", "StackBot\StackBot.njsproj", "{E7CE1022-2FF2-44E3-8057-D472019453EF}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {A8EB4436-43A2-4F57-B381-1DC8C470E16D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A8EB4436-43A2-4F57-B381-1DC8C470E16D}.Debug|Any CPU.Build.0 = Debug|Any CPU {A8EB4436-43A2-4F57-B381-1DC8C470E16D}.Release|Any CPU.ActiveCfg = Release|Any CPU {A8EB4436-43A2-4F57-B381-1DC8C470E16D}.Release|Any CPU.Build.0 = Release|Any CPU {E7CE1022-2FF2-44E3-8057-D472019453EF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E7CE1022-2FF2-44E3-8057-D472019453EF}.Debug|Any CPU.Build.0 = Debug|Any CPU {E7CE1022-2FF2-44E3-8057-D472019453EF}.Release|Any CPU.ActiveCfg = Release|Any CPU {E7CE1022-2FF2-44E3-8057-D472019453EF}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {2D37936F-4B78-43EC-A917-329B913AE10D} EndGlobalSection EndGlobal ================================================ FILE: StackOverflow-Bot/env.template ================================================ export BOTBUILDER_APP_ID= export BOTBUILDER_APP_PASSWORD= export KB_ID= export QNA_KEY= export QNA_URL= export LUIS_MODEL= export DIALOG_ANALYZER_CLIENTID= export DIALOG_ANALYZER_KEY= export DIALOG_ANALYZER_URL= export BING_SEARCH_CONFIG= export BING_SEARCH_KEY= export TEXT_ANALYTICS_KEY= export TEXT_ANALYTICS_URL= ================================================ FILE: _config.yml ================================================ theme: jekyll-theme-cayman ================================================ FILE: blog-samples/CSharp/AzureSql-StateClient/Microsoft.Bot.Sample.AzureSql/App_Start/WebApiConfig.cs ================================================ using Newtonsoft.Json; using Newtonsoft.Json.Serialization; using System; using System.Collections.Generic; using System.Linq; using System.Web.Http; namespace Microsoft.Bot.Sample.AzureSql { public static class WebApiConfig { public static void Register(HttpConfiguration config) { // Json settings config.Formatters.JsonFormatter.SerializerSettings.NullValueHandling = NullValueHandling.Ignore; config.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); config.Formatters.JsonFormatter.SerializerSettings.Formatting = Formatting.Indented; JsonConvert.DefaultSettings = () => new JsonSerializerSettings() { ContractResolver = new CamelCasePropertyNamesContractResolver(), Formatting = Newtonsoft.Json.Formatting.Indented, NullValueHandling = NullValueHandling.Ignore, }; // Web API configuration and services // Web API routes config.MapHttpAttributeRoutes(); config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{id}", defaults: new { id = RouteParameter.Optional } ); } } } ================================================ FILE: blog-samples/CSharp/AzureSql-StateClient/Microsoft.Bot.Sample.AzureSql/Controllers/MessagesController.cs ================================================ using System.Net; using System.Net.Http; using System.Threading.Tasks; using System.Web.Http; using Microsoft.Bot.Builder.Dialogs; using Microsoft.Bot.Connector; namespace Microsoft.Bot.Sample.AzureSql { [BotAuthentication] public class MessagesController : ApiController { /// /// POST: api/Messages /// Receive a message from a user and reply to it /// public async Task Post([FromBody]Activity activity) { if (activity.Type == ActivityTypes.Message) { await Conversation.SendAsync(activity, () => new Dialogs.RootDialog()); } else { await HandleSystemMessage(activity); } var response = Request.CreateResponse(HttpStatusCode.OK); return response; } private async Task HandleSystemMessage(Activity message) { if (message.Type == ActivityTypes.DeleteUserData) { // Implement user deletion here // If we handle user deletion, return a real message } else if (message.Type == ActivityTypes.ConversationUpdate) { IConversationUpdateActivity iConversationUpdated = message as IConversationUpdateActivity; if (iConversationUpdated != null) { ConnectorClient connector = new ConnectorClient(new System.Uri(message.ServiceUrl)); foreach (var member in iConversationUpdated.MembersAdded ?? System.Array.Empty()) { if (member.Id == iConversationUpdated.Recipient.Id) { var reply = message.CreateReply("I'm an IBotDataStore example. I'll increment a number, stored in Private Conversation Data, every time you send me a message."); await connector.Conversations.ReplyToActivityAsync(reply); } } } } else if (message.Type == ActivityTypes.ContactRelationUpdate) { // Handle add/remove from contact lists // Activity.From + Activity.Action represent what happened } else if (message.Type == ActivityTypes.Typing) { // Handle knowing tha the user is typing } else if (message.Type == ActivityTypes.Ping) { } return null; } } } ================================================ FILE: blog-samples/CSharp/AzureSql-StateClient/Microsoft.Bot.Sample.AzureSql/Dialogs/RootDialog.cs ================================================ using System; using System.Threading.Tasks; using Microsoft.Bot.Builder.Dialogs; using Microsoft.Bot.Connector; using Microsoft.Bot.Builder.Dialogs.Internals; namespace Microsoft.Bot.Sample.AzureSql.Dialogs { [Serializable] public class RootDialog : IDialog { public Task StartAsync(IDialogContext context) { context.Wait(MessageReceivedAsync); return Task.CompletedTask; } private async Task MessageReceivedAsync(IDialogContext context, IAwaitable result) { var privateData = context.PrivateConversationData; var privateConversationInfo = IncrementInfoCount(privateData, BotStoreType.BotPrivateConversationData.ToString()); var conversationData = context.ConversationData; var conversationInfo = IncrementInfoCount(conversationData, BotStoreType.BotConversationData.ToString()); var userData = context.UserData; var userInfo = IncrementInfoCount(userData, BotStoreType.BotUserData.ToString()); var activity = await result as Activity; // calculate something for us to return int length = (activity.Text ?? string.Empty).Length; // return our reply to the user await context.PostAsync($"You sent {activity.Text} which was {length} characters. \n\nPrivate Conversation message count: {privateConversationInfo.Count}. \n\nConversation message count: {conversationInfo.Count}.\n\nUser message count: {userInfo.Count}."); privateData.SetValue(BotStoreType.BotPrivateConversationData.ToString(), privateConversationInfo); conversationData.SetValue(BotStoreType.BotConversationData.ToString(), conversationInfo); userData.SetValue(BotStoreType.BotUserData.ToString(), userInfo); context.Wait(MessageReceivedAsync); } public class BotDataInfo { public int Count { get; set; } } private BotDataInfo IncrementInfoCount(IBotDataBag botdata, string key) { BotDataInfo info = null; if (botdata.ContainsKey(key)) { info = botdata.GetValue(key); info.Count++; } else info = new BotDataInfo() { Count = 1 }; return info; } } } ================================================ FILE: blog-samples/CSharp/AzureSql-StateClient/Microsoft.Bot.Sample.AzureSql/Global.asax ================================================ <%@ Application Codebehind="Global.asax.cs" Inherits="Microsoft.Bot.Sample.AzureSql.WebApiApplication" Language="C#" %> ================================================ FILE: blog-samples/CSharp/AzureSql-StateClient/Microsoft.Bot.Sample.AzureSql/Global.asax.cs ================================================ using Autofac; using Microsoft.Bot.Builder.Dialogs; using Microsoft.Bot.Builder.Dialogs.Internals; using Microsoft.Bot.Connector; using Microsoft.Bot.Sample.AzureSql.SqlStateService; using System.Web.Http; namespace Microsoft.Bot.Sample.AzureSql { public class WebApiApplication : System.Web.HttpApplication { protected void Application_Start() { GlobalConfiguration.Configure(WebApiConfig.Register); var builder = new ContainerBuilder(); builder.RegisterModule(new DialogModule()); var store = new SqlBotDataStore("BotDataContextConnectionString"); builder.Register(c => new CachingBotDataStore(store, CachingBotDataStoreConsistencyPolicy.LastWriteWins)) .As>() .AsSelf() .InstancePerLifetimeScope(); builder.Update(Conversation.Container); } } } ================================================ FILE: blog-samples/CSharp/AzureSql-StateClient/Microsoft.Bot.Sample.AzureSql/Microsoft.Bot.Sample.AzureSql.csproj ================================================  Debug AnyCPU 2.0 {18C42588-D8F0-48A9-A7AF-02A5BF8252A9} {349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} Library Properties Microsoft.Bot.Sample.AzureSql Microsoft.Bot.Sample.AzureSql v4.6 true true full false bin\ DEBUG;TRACE prompt 4 pdbonly true bin\ TRACE prompt 4 $(SolutionDir)\packages\Autofac.3.5.2\lib\net40\Autofac.dll $(SolutionDir)\packages\Chronic.Signed.0.3.2\lib\net40\Chronic.dll ..\packages\EntityFramework.6.1.3\lib\net45\EntityFramework.dll ..\packages\EntityFramework.6.1.3\lib\net45\EntityFramework.SqlServer.dll ..\packages\Microsoft.Bot.Builder.3.8.1.0\lib\net46\Microsoft.Bot.Builder.dll ..\packages\Microsoft.Bot.Builder.3.8.1.0\lib\net46\Microsoft.Bot.Builder.Autofac.dll ..\packages\Microsoft.Bot.Builder.3.8.1.0\lib\net46\Microsoft.Bot.Connector.dll $(SolutionDir)\packages\Microsoft.IdentityModel.Protocol.Extensions.1.0.4.403061554\lib\net45\Microsoft.IdentityModel.Protocol.Extensions.dll $(SolutionDir)\packages\Microsoft.Rest.ClientRuntime.2.3.2\lib\net45\Microsoft.Rest.ClientRuntime.dll $(SolutionDir)\packages\Microsoft.WindowsAzure.ConfigurationManager.3.1.0\lib\net40\Microsoft.WindowsAzure.Configuration.dll $(SolutionDir)\packages\Newtonsoft.Json.8.0.3\lib\net45\Newtonsoft.Json.dll $(SolutionDir)\packages\System.IdentityModel.Tokens.Jwt.4.0.4.403061554\lib\net45\System.IdentityModel.Tokens.Jwt.dll $(SolutionDir)\packages\Microsoft.AspNet.WebApi.Client.5.2.3\lib\net45\System.Net.Http.Formatting.dll $(SolutionDir)\packages\Microsoft.AspNet.WebApi.Core.5.2.3\lib\net45\System.Web.Http.dll $(SolutionDir)\packages\Microsoft.AspNet.WebApi.WebHost.5.2.3\lib\net45\System.Web.Http.WebHost.dll Designer Global.asax 201707121827490_Initial Setup.cs Designer Web.config Web.config 201707121827490_Initial Setup.cs 10.0 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) true True True 3979 / http://localhost:3980/ False False False ================================================ FILE: blog-samples/CSharp/AzureSql-StateClient/Microsoft.Bot.Sample.AzureSql/Migrations/201707121827490_Initial Setup.Designer.cs ================================================ // namespace Microsoft.Bot.Sample.AzureSql.Migrations { using System.CodeDom.Compiler; using System.Data.Entity.Migrations; using System.Data.Entity.Migrations.Infrastructure; using System.Resources; [GeneratedCode("EntityFramework.Migrations", "6.1.3-40302")] public sealed partial class InitialSetup : IMigrationMetadata { private readonly ResourceManager Resources = new ResourceManager(typeof(InitialSetup)); string IMigrationMetadata.Id { get { return "201707121827490_Initial Setup"; } } string IMigrationMetadata.Source { get { return null; } } string IMigrationMetadata.Target { get { return Resources.GetString("Target"); } } } } ================================================ FILE: blog-samples/CSharp/AzureSql-StateClient/Microsoft.Bot.Sample.AzureSql/Migrations/201707121827490_Initial Setup.cs ================================================ namespace Microsoft.Bot.Sample.AzureSql.Migrations { using System; using System.Data.Entity.Migrations; public partial class InitialSetup : DbMigration { public override void Up() { CreateTable( "dbo.SqlBotDataEntities", c => new { Id = c.Int(nullable: false, identity: true), BotStoreType = c.Int(nullable: false), BotId = c.String(), ChannelId = c.String(maxLength: 200), ConversationId = c.String(maxLength: 200), UserId = c.String(maxLength: 200), Data = c.Binary(), ETag = c.String(), ServiceUrl = c.String(), Timestamp = c.DateTimeOffset(nullable: false, precision: 7, defaultValueSql: "GETUTCDATE()"), }) .PrimaryKey(t => t.Id) .Index(t => new { t.BotStoreType, t.ChannelId, t.ConversationId }, name: "idxStoreChannelConversation") .Index(t => new { t.BotStoreType, t.ChannelId, t.ConversationId, t.UserId }, name: "idxStoreChannelConversationUser") .Index(t => new { t.BotStoreType, t.ChannelId, t.UserId }, name: "idxStoreChannelUser"); } public override void Down() { DropIndex("dbo.SqlBotDataEntities", "idxStoreChannelUser"); DropIndex("dbo.SqlBotDataEntities", "idxStoreChannelConversationUser"); DropIndex("dbo.SqlBotDataEntities", "idxStoreChannelConversation"); DropTable("dbo.SqlBotDataEntities"); } } } ================================================ FILE: blog-samples/CSharp/AzureSql-StateClient/Microsoft.Bot.Sample.AzureSql/Migrations/201707121827490_Initial Setup.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 H4sIAAAAAAAEAM1Z3W7bNhS+H7B3EHS1AakUx22XGXaLxEkKY3UcVEnvaenYIUZRLkkZ9oY92S72SHuFHeqXkvwjO24wFAEakuc7vzw8n/Lv3//0P65CZi1BSBrxgd1xzm0LuB8FlM8Hdqxmby7tjx9+/KF/G4Qr62t+rqvPoSSXA/tZqUXPdaX/DCGRTkh9Eclophw/Cl0SRO7F+fmvbqfjAkLYiGVZ/S8xVzSE5Bf8dRhxHxYqJmwcBcBkto47XoJq3ZMQ5IL4MLDHBf51pByPhAsGztUfsQDvG3Pwx1NEgQdiSX2wrStGCRrpAZvZFuE8wl10ofckwVMi4nNvgQuEPa4XgOdmhEnIXOuVx9t6eX6hvXRLwRzKj6WKwgMBO90sbG5d/Kjg20VYMbC3mAC11l4nwcUIfWMY0BuiSLplW3WlvSETWuDAHDh15DNrp/xZUWZYjfrfmTWMmcLNAYdYCYInHuIpo/5vsH6Mfgc+4DFjpnfoH+5VFnDpQUQLEGr9BWaZz6PAttyqnFsXLMQMmTQKI666F7Z1j8rJlEFRPEbEPBUJ+AQcBMYjeCBKgeAaA7IYuzt1YXQSBK0v16or2aluNCzYi1o6gZcAr7ptjcnqM/C5esbskpVt3dEVBPlKBvvEKXYGFFIi3qtl+IxxALZTExbnKTRFXDewJOKvoA47h3gFNfq+5EquKSdi3S5Ju1FvH8n8u6c+u/hPgn13VY/4ikiFDSTXhGEDvTiZzSSoo27nMAoXMS5UdffdsmVWG2kcGm20ejNH8o6RefmutG2p1zFlAQjnBl+maC4dbDVoGmJUbn69kWZSZQPtOpdOp00D7Xams+7lu/ck6L5/C913OguIxNaYNbPZVXMxhnAKonTcvIhp+X4lLMa980Ya66IPgi4xCVsROnsR9LWsiFw085dmqvkMolpFKC/gyhdL78BKbcgc6suSJ7NSrdqXInugShNT88oyypp58+XdVHeFjeVw5KbTUT5FuVvGqP6YLBaYSGOsylYsL52phm+8wyeKMMVwfblhsCisLTRh0ZI51Hb1ZQvgjgqZ+D8l+pIMg7BxrJmRLdHO9VWDXh8Fyhzk5/X/U5mXDTd1VWWw79D/EN/9JBRQGLxh8mpAJIMwYURsmEWGEYtDvm2e2SVdbVUmTnXnIMS6SdlSewxjbjBxjOUDsGqTQQWwttceNR8ATLR8rT1K2gxMjHSlPUL6lpsI6Up7BPOhNnHM9fZoxltsghnLTay+W7sd9ZvqNq5qbV6v3/xdrbN+pNBetNBaq+xnbWs/LW30sfSIbWGQljRIethaKggdfUC3jSGj6G95YEw4nWGk0hcZp8jORdp2aiT2/0MoXSkDdiCrfHWWRnWM9/KwwylUg5glihpjwgjnqNXA/jMR7Fk0WCWCWTszm9CZNRFYBj2rY/3V4rjuOC1E6sdexhb5kgj/mYifQrL62YQ6ihHmaKcJ28XhYdsuUj/WJHgH+r6Ro542AN3DA9B9uWdVOnwSjyomvm2XoxN4YjJudGOakO6jKt1k2S+6Mk0m/SK4BlsOsB3qD7FRxpYfBPg0/cT7y9auWfLjna1kO2HeRLmaPGA3nWqSpha8Kn2T0e1phBFILa+doiCP5l/NYaHvml+6+zcg6byE0N+9Ofi65EvQ/MyIz6I8fei2aVF+pJbdMSiCCSVXQtEZ8ZFTRz5ImXx4yTjxLXLmYMQnscIMXkmJHJpV+Gbf3a0/IZlVm/uThf5NnsIFNJPqmpzw5FtGYffdhvLaAqELKatZtMpTunbn6wLpPuItgbLw3cACuJ4THgGZIILJCffIEo6xDTvWZ5gTf53PfNtB9ieiGvb+DSVzQUKZYZTy+q83rv7zzYf/AOLSD7jwGQAA dbo ================================================ FILE: blog-samples/CSharp/AzureSql-StateClient/Microsoft.Bot.Sample.AzureSql/Migrations/Configuration.cs ================================================ namespace Microsoft.Bot.Sample.AzureSql.Migrations { using System; using System.Data.Entity; using System.Data.Entity.Migrations; using System.Linq; internal sealed class Configuration : DbMigrationsConfiguration { public Configuration() { AutomaticMigrationsEnabled = false; } protected override void Seed(Microsoft.Bot.Sample.AzureSql.SqlStateService.SqlBotDataContext context) { // This method will be called after migrating to the latest version. // You can use the DbSet.AddOrUpdate() helper extension method // to avoid creating duplicate seed data. E.g. // // context.People.AddOrUpdate( // p => p.FullName, // new Person { FullName = "Andrew Peters" }, // new Person { FullName = "Brice Lambson" }, // new Person { FullName = "Rowan Miller" } // ); // } } } ================================================ FILE: blog-samples/CSharp/AzureSql-StateClient/Microsoft.Bot.Sample.AzureSql/Properties/AssemblyInfo.cs ================================================ using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. [assembly: AssemblyTitle("Microsoft.Bot.Sample.AzureSql")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("Microsoft.Bot.Sample.AzureSql")] [assembly: AssemblyCopyright("Copyright © 2016")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] // Setting ComVisible to false makes the types in this assembly not visible // to COM components. If you need to access a type in this assembly from // COM, set the ComVisible attribute to true on that type. [assembly: ComVisible(false)] // The following GUID is for the ID of the typelib if this project is exposed to COM [assembly: Guid("18c42588-d8f0-48a9-a7af-02a5bf8252a9")] // Version information for an assembly consists of the following four values: // // Major Version // Minor Version // Build Number // Revision // // You can specify all the values or you can default the Revision and Build Numbers // by using the '*' as shown below: [assembly: AssemblyVersion("1.0.0.0")] [assembly: AssemblyFileVersion("1.0.0.0")] ================================================ FILE: blog-samples/CSharp/AzureSql-StateClient/Microsoft.Bot.Sample.AzureSql/SqlStateService/SqlBotDataContext.cs ================================================ using System.Data.Entity; namespace Microsoft.Bot.Sample.AzureSql.SqlStateService { public class SqlBotDataContext : DbContext { public SqlBotDataContext() : this("BotDataContextConnectionString") { } public SqlBotDataContext(string connectionStringName) : base(connectionStringName) { } public DbSet BotData { get; set; } } } ================================================ FILE: blog-samples/CSharp/AzureSql-StateClient/Microsoft.Bot.Sample.AzureSql/SqlStateService/SqlBotDataEntity.cs ================================================ using Microsoft.Bot.Builder.Dialogs; using Microsoft.Bot.Builder.Dialogs.Internals; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using System.IO; using System.IO.Compression; using System.Linq; namespace Microsoft.Bot.Sample.AzureSql.SqlStateService { public class SqlBotDataEntity : IAddress { private static readonly JsonSerializerSettings serializationSettings = new JsonSerializerSettings() { Formatting = Formatting.None, NullValueHandling = NullValueHandling.Ignore }; internal SqlBotDataEntity() { } internal SqlBotDataEntity(BotStoreType botStoreType, string botId, string channelId, string conversationId, string userId, object data) { this.BotStoreType = botStoreType; this.BotId = botId; this.ChannelId = channelId; this.ConversationId = conversationId; this.UserId = userId; this.Data = Serialize(data); } #region Fields [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int Id { get; set; } [Index("idxStoreChannelUser", 1)] [Index("idxStoreChannelConversation", 1)] [Index("idxStoreChannelConversationUser", 1)] public BotStoreType BotStoreType { get; set; } public string BotId { get; set; } [Index("idxStoreChannelConversation", 2)] [Index("idxStoreChannelUser", 2)] [Index("idxStoreChannelConversationUser", 2)] [MaxLength(200)] public string ChannelId { get; set; } [Index("idxStoreChannelConversation", 3)] [Index("idxStoreChannelConversationUser", 3)] [MaxLength(200)] public string ConversationId { get; set; } [Index("idxStoreChannelUser", 3)] [Index("idxStoreChannelConversationUser", 4)] [MaxLength(200)] public string UserId { get; set; } public byte[] Data { get; set; } public string ETag { get; set; } public string ServiceUrl { get; set; } [Required, DatabaseGenerated(DatabaseGeneratedOption.Computed)] public DateTimeOffset Timestamp { get; set; } #endregion Fields #region Methods private static byte[] Serialize(object data) { using (var cmpStream = new MemoryStream()) using (var stream = new GZipStream(cmpStream, CompressionMode.Compress)) using (var streamWriter = new StreamWriter(stream)) { var serializedJSon = JsonConvert.SerializeObject(data, serializationSettings); streamWriter.Write(serializedJSon); streamWriter.Close(); stream.Close(); return cmpStream.ToArray(); } } private static object Deserialize(byte[] bytes) { using (var stream = new MemoryStream(bytes)) using (var gz = new GZipStream(stream, CompressionMode.Decompress)) using (var streamReader = new StreamReader(gz)) { return JsonConvert.DeserializeObject(streamReader.ReadToEnd()); } } internal ObjectT GetData() { return ((JObject)Deserialize(this.Data)).ToObject(); } internal object GetData() { return Deserialize(this.Data); } internal static SqlBotDataEntity GetSqlBotDataEntity(IAddress key, BotStoreType botStoreType, SqlBotDataContext context) { SqlBotDataEntity entity = null; var query = context.BotData.OrderByDescending(d => d.Timestamp); switch (botStoreType) { case BotStoreType.BotConversationData: entity = query.FirstOrDefault(d => d.BotStoreType == botStoreType && d.ChannelId == key.ChannelId && d.ConversationId == key.ConversationId); break; case BotStoreType.BotUserData: entity = query.FirstOrDefault(d => d.BotStoreType == botStoreType && d.ChannelId == key.ChannelId && d.UserId == key.UserId); break; case BotStoreType.BotPrivateConversationData: entity = query.FirstOrDefault(d => d.BotStoreType == botStoreType && d.ChannelId == key.ChannelId && d.ConversationId == key.ConversationId && d.UserId == key.UserId); break; default: throw new ArgumentException("Unsupported bot store type!"); } return entity; } #endregion } } ================================================ FILE: blog-samples/CSharp/AzureSql-StateClient/Microsoft.Bot.Sample.AzureSql/SqlStateService/SqlBotDataStore.cs ================================================ using Microsoft.Bot.Builder.Dialogs; using Microsoft.Bot.Builder.Dialogs.Internals; using Microsoft.Bot.Connector; using System; using System.Net; using System.Threading; using System.Threading.Tasks; using System.Web; namespace Microsoft.Bot.Sample.AzureSql.SqlStateService { public class SqlBotDataStore : IBotDataStore { string _connectionStringName { get; set; } public SqlBotDataStore(string connectionStringName) { _connectionStringName = connectionStringName; } async Task IBotDataStore.LoadAsync(IAddress key, BotStoreType botStoreType, CancellationToken cancellationToken) { using (var context = new SqlBotDataContext(_connectionStringName)) { try { SqlBotDataEntity entity = SqlBotDataEntity.GetSqlBotDataEntity(key, botStoreType, context); if (entity == null) return new BotData(eTag: String.Empty, data: null); return new BotData(entity.ETag, entity.GetData()); } catch (Exception ex) { throw new HttpException((int)HttpStatusCode.InternalServerError, ex.Message); } } } async Task IBotDataStore.SaveAsync(IAddress key, BotStoreType botStoreType, BotData botData, CancellationToken cancellationToken) { SqlBotDataEntity entity = new SqlBotDataEntity(botStoreType, key.BotId, key.ChannelId, key.ConversationId, key.UserId, botData.Data) { ETag = botData.ETag, ServiceUrl = key.ServiceUrl }; using (var context = new SqlBotDataContext(_connectionStringName)) { try { if (String.IsNullOrEmpty(entity.ETag)) { context.BotData.Add(entity); } else if (entity.ETag == "*") { var foundData = SqlBotDataEntity.GetSqlBotDataEntity(key, botStoreType, context); if (botData.Data != null) { if (foundData == null) context.BotData.Add(entity); else { foundData.Data = entity.Data; foundData.ServiceUrl = entity.ServiceUrl; } } else { if (foundData != null) context.BotData.Remove(foundData); } } else { var foundData = SqlBotDataEntity.GetSqlBotDataEntity(key, botStoreType, context); if (botData.Data != null) { if (foundData == null) context.BotData.Add(entity); else { foundData.Data = entity.Data; foundData.ServiceUrl = entity.ServiceUrl; foundData.ETag = entity.ETag; } } else { if (foundData != null) context.BotData.Remove(foundData); } } context.SaveChanges(); } catch (System.Data.SqlClient.SqlException err) { throw new HttpException((int)HttpStatusCode.InternalServerError, err.Message); } } } Task IBotDataStore.FlushAsync(IAddress key, CancellationToken cancellationToken) { // Everything is saved. Flush is no-op return Task.FromResult(true); } } } ================================================ FILE: blog-samples/CSharp/AzureSql-StateClient/Microsoft.Bot.Sample.AzureSql/Web.Debug.config ================================================ ================================================ FILE: blog-samples/CSharp/AzureSql-StateClient/Microsoft.Bot.Sample.AzureSql/Web.Release.config ================================================ ================================================ FILE: blog-samples/CSharp/AzureSql-StateClient/Microsoft.Bot.Sample.AzureSql/Web.config ================================================ 
================================================ FILE: blog-samples/CSharp/AzureSql-StateClient/Microsoft.Bot.Sample.AzureSql/default.htm ================================================ 
================================================ FILE: blog-samples/CSharp/AzureSql-StateClient/Microsoft.Bot.Sample.AzureSql/packages.config ================================================  ================================================ FILE: blog-samples/CSharp/AzureSql-StateClient/Microsoft.Bot.Sample.AzureSql.sln ================================================  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 VisualStudioVersion = 15.0.26430.6 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Bot.Sample.AzureSql", "Microsoft.Bot.Sample.AzureSql\Microsoft.Bot.Sample.AzureSql.csproj", "{18C42588-D8F0-48A9-A7AF-02A5BF8252A9}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {18C42588-D8F0-48A9-A7AF-02A5BF8252A9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {18C42588-D8F0-48A9-A7AF-02A5BF8252A9}.Debug|Any CPU.Build.0 = Debug|Any CPU {18C42588-D8F0-48A9-A7AF-02A5BF8252A9}.Release|Any CPU.ActiveCfg = Release|Any CPU {18C42588-D8F0-48A9-A7AF-02A5BF8252A9}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection EndGlobal ================================================ FILE: blog-samples/CSharp/AzureSql-StateClient/README.md ================================================ # Explanation A example State Client using Sql Server for storage. # Setup 1) Register a bot with the Bot Framework at http://dev.botframework.com/ and add the AppId and AppPassword to Microsoft.Bot.Sample.AzureSql\Web.config. 2) Retrieve a WebChat secret from https://dev.botframework.com/bots/channels?id=[YourBotId]&channelId=webchat and add it to Microsoft.Bot.Sample.AzureSql\default.htm in place of [YourWebChatSecret]. 3) Create an Sql Server database and add the connection string to Microsoft.Bot.Sample.AzureSql\Web.config, overwriting the current [BotDataContextConnectionString]. 4) Execute 'update-database' from the Nuget Package Manager Console. This will create the SqlBotDataEntities table. 5) Publish the Bot as an Azure App Service, and add the public endpoint (https://[YourHostUrl]/api/messages) to the Bot Framework portal settings page. Make sure to use https instead of http in the url. ================================================ FILE: blog-samples/CSharp/Bot-Feedback-Sample/Bot-Feedback-Sample/App_Start/WebApiConfig.cs ================================================ using System.Web.Http; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; namespace Bot_Feedback_Sample { public static class WebApiConfig { public static void Register(HttpConfiguration config) { // Json settings config.Formatters.JsonFormatter.SerializerSettings.NullValueHandling = NullValueHandling.Ignore; config.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); config.Formatters.JsonFormatter.SerializerSettings.Formatting = Formatting.Indented; JsonConvert.DefaultSettings = () => new JsonSerializerSettings() { ContractResolver = new CamelCasePropertyNamesContractResolver(), Formatting = Newtonsoft.Json.Formatting.Indented, NullValueHandling = NullValueHandling.Ignore, }; // Web API configuration and services // Web API routes config.MapHttpAttributeRoutes(); config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{id}", defaults: new { id = RouteParameter.Optional } ); } } } ================================================ FILE: blog-samples/CSharp/Bot-Feedback-Sample/Bot-Feedback-Sample/ApplicationInsights.config ================================================ search|spider|crawl|Bot|Monitor|AlwaysOn core.windows.net core.chinacloudapi.cn core.cloudapi.de core.usgovcloudapi.net localhost 127.0.0.1 System.Web.Handlers.TransferRequestHandler Microsoft.VisualStudio.Web.PageInspector.Runtime.Tracing.RequestDataHttpHandler System.Web.StaticFileHandler System.Web.Handlers.AssemblyResourceLoader System.Web.Optimization.BundleHandler System.Web.Script.Services.ScriptHandlerFactory System.Web.Handlers.TraceHandler System.Web.Services.Discovery.DiscoveryRequestHandler System.Web.HttpDebugHandler 5 Event 5 Event 5728d8e7-0ce9-4571-b266-3a8a75cbb051 ================================================ FILE: blog-samples/CSharp/Bot-Feedback-Sample/Bot-Feedback-Sample/Bot-Feedback-Sample.csproj ================================================  Debug AnyCPU 2.0 {A8BA1066-5695-4D71-ABB4-65E5A5E0C3D4} {349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} Library Properties Bot_Feedback_Sample Bot Application v4.6 true /subscriptions/3e2f81a1-d5b4-47c0-951e-5611a012e1a3/resourcegroups/Default-ApplicationInsights-EastUS/providers/microsoft.insights/components/Bot-Feedback-Sample /subscriptions/3e2f81a1-d5b4-47c0-951e-5611a012e1a3/resourcegroups/Default-ApplicationInsights-EastUS/providers/microsoft.insights/components/Bot-Feedback-Sample true full false bin\ DEBUG;TRACE prompt 4 pdbonly true bin\ TRACE prompt 4 $(SolutionDir)\packages\Autofac.3.5.2\lib\net40\Autofac.dll $(SolutionDir)\packages\Chronic.Signed.0.3.2\lib\net40\Chronic.dll ..\packages\Microsoft.ApplicationInsights.Agent.Intercept.2.4.0\lib\net45\Microsoft.AI.Agent.Intercept.dll ..\packages\Microsoft.ApplicationInsights.DependencyCollector.2.4.1\lib\net45\Microsoft.AI.DependencyCollector.dll ..\packages\Microsoft.ApplicationInsights.PerfCounterCollector.2.4.1\lib\net45\Microsoft.AI.PerfCounterCollector.dll ..\packages\Microsoft.ApplicationInsights.WindowsServer.TelemetryChannel.2.4.0\lib\net45\Microsoft.AI.ServerTelemetryChannel.dll ..\packages\Microsoft.ApplicationInsights.Web.2.4.1\lib\net45\Microsoft.AI.Web.dll ..\packages\Microsoft.ApplicationInsights.WindowsServer.2.4.1\lib\net45\Microsoft.AI.WindowsServer.dll ..\packages\Microsoft.ApplicationInsights.2.4.0\lib\net46\Microsoft.ApplicationInsights.dll ..\packages\Microsoft.AspNet.TelemetryCorrelation.1.0.0\lib\net45\Microsoft.AspNet.TelemetryCorrelation.dll ..\packages\Microsoft.Bot.Builder.3.9.0.0\lib\net46\Microsoft.Bot.Builder.dll ..\packages\Microsoft.Bot.Builder.3.9.0.0\lib\net46\Microsoft.Bot.Builder.Autofac.dll ..\packages\Microsoft.Bot.Builder.CognitiveServices.1.1.1\lib\net46\Microsoft.Bot.Builder.CognitiveServices.QnAMaker.dll ..\packages\Microsoft.Bot.Builder.3.9.0.0\lib\net46\Microsoft.Bot.Connector.dll ..\packages\Microsoft.IdentityModel.Protocol.Extensions.1.0.4.403061554\lib\net45\Microsoft.IdentityModel.Protocol.Extensions.dll $(SolutionDir)\packages\Microsoft.Rest.ClientRuntime.2.3.2\lib\net45\Microsoft.Rest.ClientRuntime.dll $(SolutionDir)\packages\Microsoft.WindowsAzure.ConfigurationManager.3.1.0\lib\net40\Microsoft.WindowsAzure.Configuration.dll $(SolutionDir)\packages\Newtonsoft.Json.8.0.3\lib\net45\Newtonsoft.Json.dll ..\packages\System.Diagnostics.DiagnosticSource.4.4.0\lib\net46\System.Diagnostics.DiagnosticSource.dll ..\packages\System.IdentityModel.Tokens.Jwt.4.0.4.403061554\lib\net45\System.IdentityModel.Tokens.Jwt.dll $(SolutionDir)\packages\Microsoft.AspNet.WebApi.Client.5.2.3\lib\net45\System.Net.Http.Formatting.dll $(SolutionDir)\packages\Microsoft.AspNet.WebApi.Core.5.2.3\lib\net45\System.Web.Http.dll $(SolutionDir)\packages\Microsoft.AspNet.WebApi.WebHost.5.2.3\lib\net45\System.Web.Http.WebHost.dll Designer Global.asax PreserveNewest Web.config Web.config 10.0 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) true True True 3979 / http://localhost:3979/ False False False ================================================ FILE: blog-samples/CSharp/Bot-Feedback-Sample/Bot-Feedback-Sample/Connected Services/Application Insights/ConnectedService.json ================================================ { "ProviderId": "Microsoft.ApplicationInsights.ConnectedService.ConnectedServiceProvider", "Version": "8.8.712.1", "GettingStartedDocument": { "Uri": "" } } ================================================ FILE: blog-samples/CSharp/Bot-Feedback-Sample/Bot-Feedback-Sample/Controllers/MessagesController.cs ================================================ using System.Net; using System.Net.Http; using System.Threading.Tasks; using System.Web.Http; using Microsoft.Bot.Builder.Dialogs; using Microsoft.Bot.Connector; namespace Bot_Feedback_Sample { [BotAuthentication] public class MessagesController : ApiController { /// /// POST: api/Messages /// Receive a message from a user and reply to it /// public async Task Post([FromBody]Activity activity) { if (activity.Type == ActivityTypes.Message) { await Conversation.SendAsync(activity, () => new Dialogs.QnaDialog()); } else { HandleSystemMessage(activity); } var response = Request.CreateResponse(HttpStatusCode.OK); return response; } private Activity HandleSystemMessage(Activity message) { if (message.Type == ActivityTypes.DeleteUserData) { // Implement user deletion here // If we handle user deletion, return a real message } else if (message.Type == ActivityTypes.ConversationUpdate) { // Handle conversation state changes, like members being added and removed // Use Activity.MembersAdded and Activity.MembersRemoved and Activity.Action for info // Not available in all channels } else if (message.Type == ActivityTypes.ContactRelationUpdate) { // Handle add/remove from contact lists // Activity.From + Activity.Action represent what happened } else if (message.Type == ActivityTypes.Typing) { // Handle knowing tha the user is typing } else if (message.Type == ActivityTypes.Ping) { } return null; } } } ================================================ FILE: blog-samples/CSharp/Bot-Feedback-Sample/Bot-Feedback-Sample/Dialogs/FeedbackDialog.cs ================================================ using System; using System.Collections.Generic; using Microsoft.Bot.Builder.Dialogs; using Microsoft.Bot.Connector; using System.Threading.Tasks; using Microsoft.ApplicationInsights; namespace Bot_Feedback_Sample { [Serializable] public class FeedbackDialog : IDialog { private string qnaURL; private string userQuestion; public FeedbackDialog(string url, string question) { // keep track of data associated with feedback qnaURL = url; userQuestion = question; } public async Task StartAsync(IDialogContext context) { var feedback = ((Activity)context.Activity).CreateReply("Did you find what you need?"); feedback.SuggestedActions = new SuggestedActions() { Actions = new List() { new CardAction(){ Title = "👍", Type=ActionTypes.PostBack, Value=$"yes-positive-feedback" }, new CardAction(){ Title = "👎", Type=ActionTypes.PostBack, Value=$"no-negative-feedback" } } }; await context.PostAsync(feedback); context.Wait(this.MessageReceivedAsync); } public async Task MessageReceivedAsync(IDialogContext context, IAwaitable result) { var userFeedback = await result; if (userFeedback.Text.Contains("yes-positive-feedback") || userFeedback.Text.Contains("no-negative-feedback")) { // create telemetry client to post to Application Insights TelemetryClient telemetry = new TelemetryClient(); if (userFeedback.Text.Contains("yes-positive-feedback")) { // post feedback to App Insights var properties = new Dictionary { {"Question", userQuestion }, {"URL", qnaURL }, {"Vote", "Yes" } // add properties relevant to your bot }; telemetry.TrackEvent("Yes-Vote", properties); } else if (userFeedback.Text.Contains("no-negative-feedback")) { // post feedback to App Insights } await context.PostAsync("Thanks for your feedback!"); context.Done(null); } else { // no feedback, return to QnA dialog context.Done(userFeedback); } } } } ================================================ FILE: blog-samples/CSharp/Bot-Feedback-Sample/Bot-Feedback-Sample/Dialogs/QnADialog.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using System.Configuration; using Microsoft.Bot.Connector; using Microsoft.Bot.Builder.Dialogs; using Microsoft.Bot.Builder.CognitiveServices.QnAMaker; using Newtonsoft.Json.Linq; namespace Bot_Feedback_Sample.Dialogs { [Serializable] public class QnaDialog : QnAMakerDialog { public QnaDialog() : base(new QnAMakerService(new QnAMakerAttribute(ConfigurationManager.AppSettings["QnaSubscriptionKey"], ConfigurationManager.AppSettings["QnaKnowledgebaseId"], "Sorry, I couldn't find an answer for that", 0.5))) { } protected override async Task RespondFromQnAMakerResultAsync(IDialogContext context, IMessageActivity message, QnAMakerResults result) { // answer is a string var answer = result.Answers.First().Answer; Activity reply = ((Activity)context.Activity).CreateReply(); string[] qnaAnswerData = answer.Split(';'); int dataSize = qnaAnswerData.Length; string title = qnaAnswerData[0]; string description = qnaAnswerData[1]; string url = qnaAnswerData[2]; string imageURL = qnaAnswerData[3]; HeroCard card = new HeroCard { Title = title, Subtitle = description, }; card.Buttons = new List { new CardAction(ActionTypes.OpenUrl, "Learn More", value: url) }; card.Images = new List { new CardImage( url = imageURL) }; reply.Attachments.Add(card.ToAttachment()); await context.PostAsync(reply); } protected override async Task DefaultWaitNextMessageAsync(IDialogContext context, IMessageActivity message, QnAMakerResults result) { // get the URL var answer = result.Answers.First().Answer; string[] qnaAnswerData = answer.Split(';'); string qnaURL = qnaAnswerData[2]; // pass user's question var userQuestion = (context.Activity as Activity).Text; context.Call(new FeedbackDialog(qnaURL, userQuestion), ResumeAfterFeedback); } private async Task ResumeAfterFeedback(IDialogContext context, IAwaitable result) { if(await result != null) { await MessageReceivedAsync(context, result); } else { context.Done(null); } } } } ================================================ FILE: blog-samples/CSharp/Bot-Feedback-Sample/Bot-Feedback-Sample/Global.asax ================================================ <%@ Application Codebehind="Global.asax.cs" Inherits="Bot_Feedback_Sample.WebApiApplication" Language="C#" %> ================================================ FILE: blog-samples/CSharp/Bot-Feedback-Sample/Bot-Feedback-Sample/Global.asax.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Http; using System.Web.Routing; namespace Bot_Feedback_Sample { public class WebApiApplication : System.Web.HttpApplication { protected void Application_Start() { GlobalConfiguration.Configure(WebApiConfig.Register); } } } ================================================ FILE: blog-samples/CSharp/Bot-Feedback-Sample/Bot-Feedback-Sample/Properties/AssemblyInfo.cs ================================================ using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. [assembly: AssemblyTitle("Bot_Feedback_Sample")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("Bot_Feedback_Sample")] [assembly: AssemblyCopyright("Copyright © 2016")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] // Setting ComVisible to false makes the types in this assembly not visible // to COM components. If you need to access a type in this assembly from // COM, set the ComVisible attribute to true on that type. [assembly: ComVisible(false)] // The following GUID is for the ID of the typelib if this project is exposed to COM [assembly: Guid("2e57f6e6-e098-4456-aa2a-58ff8d7d8821")] // Version information for an assembly consists of the following four values: // // Major Version // Minor Version // Build Number // Revision // // You can specify all the values or you can default the Revision and Build Numbers // by using the '*' as shown below: [assembly: AssemblyVersion("1.0.0.0")] [assembly: AssemblyFileVersion("1.0.0.0")] ================================================ FILE: blog-samples/CSharp/Bot-Feedback-Sample/Bot-Feedback-Sample/Web.Debug.config ================================================ ================================================ FILE: blog-samples/CSharp/Bot-Feedback-Sample/Bot-Feedback-Sample/Web.Release.config ================================================ ================================================ FILE: blog-samples/CSharp/Bot-Feedback-Sample/Bot-Feedback-Sample/Web.config ================================================  ================================================ FILE: blog-samples/CSharp/Bot-Feedback-Sample/Bot-Feedback-Sample/default.htm ================================================ 

Bot_Feedback_Sample

Describe your bot here and your terms of use etc.

Visit Bot Framework to register your bot. When you register it, remember to set your bot's endpoint to

https://your_bots_hostname/api/messages

================================================ FILE: blog-samples/CSharp/Bot-Feedback-Sample/Bot-Feedback-Sample/packages.config ================================================  ================================================ FILE: blog-samples/CSharp/Bot-Feedback-Sample/Bot-Feedback-Sample.sln ================================================  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 VisualStudioVersion = 15.0.26730.15 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Bot-Feedback-Sample", "Bot-Feedback-Sample\Bot-Feedback-Sample.csproj", "{A8BA1066-5695-4D71-ABB4-65E5A5E0C3D4}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {A8BA1066-5695-4D71-ABB4-65E5A5E0C3D4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A8BA1066-5695-4D71-ABB4-65E5A5E0C3D4}.Debug|Any CPU.Build.0 = Debug|Any CPU {A8BA1066-5695-4D71-ABB4-65E5A5E0C3D4}.Release|Any CPU.ActiveCfg = Release|Any CPU {A8BA1066-5695-4D71-ABB4-65E5A5E0C3D4}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {F417B28C-F273-42B3-8A2F-01C59DCEB6B6} EndGlobalSection EndGlobal ================================================ FILE: blog-samples/CSharp/Bot-Feedback-Sample/README.md ================================================ # QnA Bot sample - Suggested Actions to handle user feedback, with Azure Application Insights This bot sample using the .NET SDK is a continuation of the ['QnA Rich Cards'](https://github.com/Microsoft/BotFramework-Samples/tree/master/blog-samples/CSharp/Qna-Rich-Cards) sample within this repo. This bot sample was developed to accompany the Bot Framework blog post - [QnA revisited, with Suggested Actions and App Insights](https://blog.botframework.com/2017/09/28/qna-maker-revisited-suggested-actions-app-insights/) This sample aims to: 1. Demonstrate ease and flexibility of [Suggested Actions](https://docs.microsoft.com/en-us/bot-framework/dotnet/bot-builder-dotnet-add-suggested-actions) within the Bot Builder SDK, using Suggested Actions to implement a user 'feedback' thumbs up/down feature. ![Feedback with Suggested Actions][pic1] 2. Demonstrate how to add [Application Insights](https://azure.microsoft.com/en-us/services/application-insights/) to a bot project, and track custom events. ![App Insights Metrics][pic2] > **Note**: The application insights configuration for this sample is for demonstration only. Application Insights will not work on cloned/forked copies of this repo, and will need to be added to your own project. Don't worry, it's easy - just [click here](https://docs.microsoft.com/en-us/azure/application-insights/app-insights-asp-net) to read about how. ## Prerequisites - [Visual Studio 2015 or 2017 Community](https://www.visualstudio.com/downloads/) - [Bot Application Template](http://aka.ms/bf-bc-vstemplate) - [BotBuilder-CognitiveServices](https://www.nuget.org/packages/Microsoft.Bot.Builder.CognitiveServices/) NuGet package - [Bot Framework Emulator](https://docs.microsoft.com/en-us/bot-framework/debug-bots-emulator) [pic1]: ../../images/suggested-actions-feedback.png [pic2]: ../../images/app-insights-metrics.png ## Notes: - You will need to add Application Insights to your own project, [click here](https://docs.microsoft.com/en-us/azure/application-insights/app-insights-asp-net) to read how. - You will need to publish your own QnA knowledge base service - [click here](https://qnamaker.ai/Documentation/Quickstart) for the QnA maker overview, or you can review [this blog post](https://blog.botframework.com/2017/08/25/qna-maker-rich-card-attachments-net/) which walks you through how to setup and deploy your own QnA service. ================================================ FILE: blog-samples/CSharp/BotStateExport/.gitignore ================================================ # User-specific files *.suo *.user *.sln.docstates # Build results [Dd]ebug/ [Rr]elease/ x64/ [Bb]in/ [Oo]bj/ # build folder is nowadays used for build scripts and should not be ignored #build/ # NuGet Packages *.nupkg # The packages folder can be ignored because of Package Restore **/packages/* # except build/, which is used as an MSBuild target. !**/packages/build/ # Uncomment if necessary however generally it will be regenerated when needed #!**/packages/repositories.config # MSTest test Results [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* *_i.c *_p.c *.ilk *.meta *.obj *.pch *.pdb *.pgc *.pgd *.rsp *.sbr *.tlb *.tli *.tlh *.tmp *.tmp_proj *.log *.vspscc *.vssscc .builds *.pidb *.log *.scc # OS generated files # .DS_Store* Icon? # Visual Studio profiler *.psess *.vsp *.vspx # ReSharper is a .NET coding add-in _ReSharper*/ *.[Rr]e[Ss]harper # DocProject is a documentation generator add-in DocProject/buildhelp/ DocProject/Help/*.HxT DocProject/Help/*.HxC DocProject/Help/*.hhc DocProject/Help/*.hhk DocProject/Help/*.hhp DocProject/Help/Html2 DocProject/Help/html # Click-Once directory publish/ # Publish Web Output *.Publish.xml # Windows Azure Build Output csx *.build.csdef # Windows Store app package directory AppPackages/ # Others *.Cache ClientBin/ [Ss]tyle[Cc]op.* ~$* *~ *.dbmdl *.[Pp]ublish.xml *.pfx *.publishsettings modulesbin/ tempbin/ # EPiServer Site file (VPP) AppData/ # RIA/Silverlight projects Generated_Code/ # Backup & report files from converting an old project file to a newer # Visual Studio version. Backup files are not needed, because we have git ;-) _UpgradeReport_Files/ Backup*/ UpgradeLog*.XML UpgradeLog*.htm # vim *.txt~ *.swp *.swo # svn .svn # Remainings from resolvings conflicts in Source Control *.orig # SQL Server files **/App_Data/*.mdf **/App_Data/*.ldf **/App_Data/*.sdf #LightSwitch generated files GeneratedArtifacts/ _Pvt_Extensions/ ModelManifest.xml # ========================= # Windows detritus # ========================= # Windows image file caches Thumbs.db ehthumbs.db # Folder config file Desktop.ini # Recycle Bin used on file shares $RECYCLE.BIN/ # Mac desktop service store files .DS_Store # SASS Compiler cache .sass-cache # Visual Studio 2014 CTP **/*.sln.ide # Visual Studio temp something .vs/ # dotnet stuff project.lock.json # VS 2015+ *.vc.vc.opendb *.vc.db # Rider .idea/ # Output folder used by Webpack or other FE stuff **/node_modules/* **/wwwroot/* # SpecFlow specific *.feature.cs *.feature.xlsx.* *.Specs_*.html ##### # End of core ignore list, below put you custom 'per project' settings (patterns or path) ##### ================================================ FILE: blog-samples/CSharp/BotStateExport/BotStateExport/BotStateExport/App.config ================================================  ================================================ FILE: blog-samples/CSharp/BotStateExport/BotStateExport/BotStateExport/BotStateExport.csproj ================================================  Debug AnyCPU {DA593586-7A94-43D4-8EF1-0474EFF6D2E0} Exe BotStateExport BotStateExport v4.6.1 512 true AnyCPU true full false bin\Debug\ DEBUG;TRACE prompt 4 AnyCPU pdbonly true bin\Release\ TRACE prompt 4 ..\packages\Autofac.3.5.2\lib\net40\Autofac.dll True ..\packages\Chronic.Signed.0.3.2\lib\net40\Chronic.dll ..\packages\CommandLineParser.2.2.1\lib\net45\CommandLine.dll ..\packages\Microsoft.Azure.CosmosDB.Table.1.1.1\lib\net45\Microsoft.Azure.CosmosDB.Table.dll ..\packages\Microsoft.Azure.DocumentDB.Core.1.9.1\lib\netstandard1.6\Microsoft.Azure.DocumentDB.Core.dll ..\packages\Microsoft.Azure.KeyVault.Core.1.0.0\lib\net40\Microsoft.Azure.KeyVault.Core.dll True ..\packages\Microsoft.Azure.Storage.Common.9.0.0.1-preview\lib\net45\Microsoft.Azure.Storage.Common.dll ..\packages\Microsoft.Bot.Builder.3.15.0\lib\net46\Microsoft.Bot.Builder.dll True ..\packages\Microsoft.Bot.Builder.3.15.0\lib\net46\Microsoft.Bot.Builder.Autofac.dll True ..\packages\Microsoft.Bot.Connector.3.15.0\lib\net46\Microsoft.Bot.Connector.dll True ..\packages\Microsoft.Data.Edm.5.8.2\lib\net40\Microsoft.Data.Edm.dll True ..\packages\Microsoft.Data.OData.5.8.2\lib\net40\Microsoft.Data.OData.dll True ..\packages\Microsoft.Data.Services.Client.5.8.2\lib\net40\Microsoft.Data.Services.Client.dll True ..\packages\Microsoft.IdentityModel.Logging.1.1.4\lib\net451\Microsoft.IdentityModel.Logging.dll True ..\packages\Microsoft.IdentityModel.Protocols.2.1.4\lib\net451\Microsoft.IdentityModel.Protocols.dll True ..\packages\Microsoft.IdentityModel.Protocols.OpenIdConnect.2.1.4\lib\net451\Microsoft.IdentityModel.Protocols.OpenIdConnect.dll True ..\packages\Microsoft.IdentityModel.Tokens.5.1.4\lib\net451\Microsoft.IdentityModel.Tokens.dll True ..\packages\Microsoft.OData.Core.7.2.0\lib\portable-net45+win8+wpa81\Microsoft.OData.Core.dll True ..\packages\Microsoft.OData.Edm.7.2.0\lib\portable-net45+win8+wpa81\Microsoft.OData.Edm.dll True ..\packages\Microsoft.Rest.ClientRuntime.2.3.11\lib\net452\Microsoft.Rest.ClientRuntime.dll ..\packages\Microsoft.Spatial.7.2.0\lib\portable-net45+win8+wpa81\Microsoft.Spatial.dll True ..\packages\Microsoft.Win32.Primitives.4.0.1\lib\net46\Microsoft.Win32.Primitives.dll ..\packages\Microsoft.WindowsAzure.ConfigurationManager.3.2.3\lib\net40\Microsoft.WindowsAzure.Configuration.dll ..\packages\WindowsAzure.Storage.9.1.0\lib\net45\Microsoft.WindowsAzure.Storage.dll True ..\packages\Newtonsoft.Json.11.0.2\lib\net45\Newtonsoft.Json.dll ..\packages\System.AppContext.4.1.0\lib\net46\System.AppContext.dll True ..\packages\System.Collections.NonGeneric.4.0.1\lib\net46\System.Collections.NonGeneric.dll ..\packages\System.Collections.Specialized.4.0.1\lib\net46\System.Collections.Specialized.dll ..\packages\System.Console.4.0.0\lib\net46\System.Console.dll True ..\packages\System.Diagnostics.DiagnosticSource.4.0.0\lib\net46\System.Diagnostics.DiagnosticSource.dll ..\packages\System.Diagnostics.TraceSource.4.0.0\lib\net46\System.Diagnostics.TraceSource.dll True ..\packages\System.Globalization.Calendars.4.0.1\lib\net46\System.Globalization.Calendars.dll ..\packages\System.IdentityModel.Tokens.Jwt.5.1.4\lib\net451\System.IdentityModel.Tokens.Jwt.dll True ..\packages\System.IO.Compression.4.1.0\lib\net46\System.IO.Compression.dll ..\packages\System.IO.Compression.ZipFile.4.0.1\lib\net46\System.IO.Compression.ZipFile.dll ..\packages\System.IO.FileSystem.4.0.1\lib\net46\System.IO.FileSystem.dll ..\packages\System.IO.FileSystem.Primitives.4.0.1\lib\net46\System.IO.FileSystem.Primitives.dll ..\packages\System.Net.Http.4.1.0\lib\net46\System.Net.Http.dll ..\packages\Microsoft.AspNet.WebApi.Client.5.2.3\lib\net45\System.Net.Http.Formatting.dll True ..\packages\System.Net.NetworkInformation.4.1.0\lib\net46\System.Net.NetworkInformation.dll ..\packages\System.Net.Security.4.0.0\lib\net46\System.Net.Security.dll True ..\packages\System.Net.Sockets.4.1.0\lib\net46\System.Net.Sockets.dll ..\packages\System.Reflection.TypeExtensions.4.1.0\lib\net46\System.Reflection.TypeExtensions.dll True ..\packages\System.Runtime.InteropServices.RuntimeInformation.4.0.0\lib\net45\System.Runtime.InteropServices.RuntimeInformation.dll True ..\packages\System.Runtime.Serialization.Primitives.4.1.1\lib\net46\System.Runtime.Serialization.Primitives.dll ..\packages\System.Security.Cryptography.Algorithms.4.2.0\lib\net461\System.Security.Cryptography.Algorithms.dll ..\packages\System.Security.Cryptography.Encoding.4.0.0\lib\net46\System.Security.Cryptography.Encoding.dll True ..\packages\System.Security.Cryptography.Primitives.4.0.0\lib\net46\System.Security.Cryptography.Primitives.dll True ..\packages\System.Security.Cryptography.X509Certificates.4.1.0\lib\net461\System.Security.Cryptography.X509Certificates.dll ..\packages\System.Security.SecureString.4.0.0\lib\net46\System.Security.SecureString.dll True ..\packages\System.Spatial.5.8.2\lib\net40\System.Spatial.dll True ..\packages\Microsoft.AspNet.WebApi.Core.5.2.3\lib\net45\System.Web.Http.dll True This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. ================================================ FILE: blog-samples/CSharp/BotStateExport/BotStateExport/BotStateExport/DocumentDbBotDataStore.cs ================================================ // // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. // // Microsoft Bot Framework: http://botframework.com // // Bot Builder SDK Github: // https://github.com/Microsoft/BotBuilder // // Copyright (c) Microsoft Corporation // All rights reserved. // // MIT License: // 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. // using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Threading; using System.Threading.Tasks; using System.Web; using Microsoft.Azure.Documents; using Microsoft.Azure.Documents.Client; using Microsoft.Azure.Documents.Linq; using Microsoft.Bot.Builder.Dialogs; using Microsoft.Bot.Builder.Dialogs.Internals; using Microsoft.Bot.Builder.Internals.Fibers; using Microsoft.Bot.Connector; using Newtonsoft.Json; namespace Microsoft.Bot.Builder.Azure { /// /// Implementation using Azure DocumentDb /// public class DocumentDbBotDataStore : IBotDataStore { private const string entityKeyParameterName = "@entityKey"; private static readonly TimeSpan MaxInitTime = TimeSpan.FromSeconds(5); private readonly IDocumentClient documentClient; private readonly string databaseId; private readonly string collectionId; /// /// Creates an instance of the that uses the Azure DocumentDb. /// /// The DocumentDb client to use. /// The name of the DocumentDb database to use. /// The name of the DocumentDb collection to use. public DocumentDbBotDataStore(IDocumentClient documentClient, string databaseId = "botdb", string collectionId = "botcollection") { SetField.NotNull(out this.databaseId, nameof(databaseId), databaseId); SetField.NotNull(out this.collectionId, nameof(collectionId), collectionId); this.documentClient = documentClient; this.databaseId = databaseId; this.collectionId = collectionId; CreateDatabaseIfNotExistsAsync().GetAwaiter().GetResult(); CreateCollectionIfNotExistsAsync().GetAwaiter().GetResult(); } /// /// Creates an instance of the that uses the Azure DocumentDb. /// /// The service endpoint to use to create the client. /// The authorization key or resource token to use to create the client. /// The name of the DocumentDb database to use. /// The name of the DocumentDb collection to use. /// The service endpoint can be obtained from the Azure Management Portal. If you /// are connecting using one of the Master Keys, these can be obtained along with /// the endpoint from the Azure Management Portal If however you are connecting as /// a specific DocumentDB User, the value passed to authKeyOrResourceToken is the /// ResourceToken obtained from the permission feed for the user. /// Using Direct connectivity, wherever possible, is recommended. public DocumentDbBotDataStore(Uri serviceEndpoint, string authKey, string databaseId = "botdb", string collectionId = "botcollection") : this(new DocumentClient(serviceEndpoint, authKey), databaseId, collectionId) { } async Task IBotDataStore.LoadAsync(IAddress key, BotStoreType botStoreType, CancellationToken cancellationToken) { try { var entityKey = DocDbBotDataEntity.GetEntityKey(key, botStoreType); // query to retrieve the document if it exists SqlQuerySpec querySpec = new SqlQuerySpec( queryText: $"SELECT * FROM {collectionId} b WHERE (b.id = {entityKeyParameterName})", parameters: new SqlParameterCollection() { new SqlParameter(entityKeyParameterName, entityKey) }); var collectionUri = UriFactory.CreateDocumentCollectionUri(databaseId, collectionId); var query = documentClient.CreateDocumentQuery(collectionUri, querySpec) .AsDocumentQuery(); var feedResponse = await query.ExecuteNextAsync(CancellationToken.None); Document document = feedResponse.FirstOrDefault(); if (document != null) { // The document, of type IDynamicMetaObjectProvider, has a dynamic nature, // similar to DynamicTableEntity in Azure storage. When casting to a static type, properties that exist in the static type will be // populated from the dynamic type. DocDbBotDataEntity entity = (dynamic)document; return new BotData(document?.ETag, entity?.Data); } else { // the document does not exist in the database, return an empty BotData object return new BotData(string.Empty, null); } } catch (DocumentClientException e) { if (e.StatusCode.HasValue && e.StatusCode.Value == HttpStatusCode.NotFound) { return new BotData(string.Empty, null); } throw new HttpException(e.StatusCode.HasValue ? (int)e.StatusCode.Value : 0, e.Message, e); } } async Task IBotDataStore.SaveAsync(IAddress key, BotStoreType botStoreType, BotData botData, CancellationToken cancellationToken) { try { var requestOptions = new RequestOptions() { AccessCondition = new AccessCondition() { Type = AccessConditionType.IfMatch, Condition = botData.ETag } }; var entity = new DocDbBotDataEntity(key, botStoreType, botData); var entityKey = DocDbBotDataEntity.GetEntityKey(key, botStoreType); if (string.IsNullOrEmpty(botData.ETag)) { await documentClient.CreateDocumentAsync(UriFactory.CreateDocumentCollectionUri(databaseId, collectionId), entity, requestOptions); } else if (botData.ETag == "*") { if (botData.Data != null) { await documentClient.UpsertDocumentAsync(UriFactory.CreateDocumentCollectionUri(databaseId, collectionId), entity, requestOptions); } else { await documentClient.DeleteDocumentAsync(UriFactory.CreateDocumentUri(databaseId, collectionId, entityKey), requestOptions); } } else { if (botData.Data != null) { await documentClient.ReplaceDocumentAsync(UriFactory.CreateDocumentUri(databaseId, collectionId, entityKey), entity, requestOptions); } else { await documentClient.DeleteDocumentAsync(UriFactory.CreateDocumentUri(databaseId, collectionId, entityKey), requestOptions); } } } catch (DocumentClientException e) { if (e.StatusCode.HasValue && e.StatusCode.Value == HttpStatusCode.Conflict) { throw new HttpException((int)HttpStatusCode.PreconditionFailed, e.Message, e); } throw new HttpException(e.StatusCode.HasValue ? (int)e.StatusCode.Value : 0, e.Message, e); } } Task IBotDataStore.FlushAsync(IAddress key, CancellationToken cancellationToken) { // Everything is saved. Flush is no-op return Task.FromResult(true); } private async Task CreateDatabaseIfNotExistsAsync() { try { await documentClient.ReadDatabaseAsync(UriFactory.CreateDatabaseUri(databaseId)); } catch (DocumentClientException e) { if (e.StatusCode == HttpStatusCode.NotFound) { await documentClient.CreateDatabaseAsync(new Database { Id = databaseId }); } else { throw; } } } private async Task CreateCollectionIfNotExistsAsync() { try { await documentClient.ReadDocumentCollectionAsync(UriFactory.CreateDocumentCollectionUri(databaseId, collectionId)); } catch (DocumentClientException e) { if (e.StatusCode == System.Net.HttpStatusCode.NotFound) { await documentClient.CreateDocumentCollectionAsync( UriFactory.CreateDatabaseUri(databaseId), new DocumentCollection { Id = collectionId }); } else { throw; } } } } internal class BotDataDocDbKey { public BotDataDocDbKey(string partition, string row) { PartitionKey = partition; RowKey = row; } public string PartitionKey { get; private set; } public string RowKey { get; private set; } } internal class DocDbBotDataEntity { internal const int MAX_KEY_LENGTH = 254; public DocDbBotDataEntity() { } internal DocDbBotDataEntity(IAddress key, BotStoreType botStoreType, BotData botData) { this.Id = GetEntityKey(key, botStoreType); this.BotId = key.BotId; this.ChannelId = key.ChannelId; this.ConversationId = key.ConversationId; this.UserId = key.UserId; this.Data = botData.Data; } public static string GetEntityKey(IAddress key, BotStoreType botStoreType) { string entityKey; switch (botStoreType) { case BotStoreType.BotConversationData: entityKey = $"{key.ChannelId}:conversation{key.ConversationId.SanitizeForAzureKeys()}"; return TruncateEntityKey(entityKey); case BotStoreType.BotUserData: entityKey = $"{key.ChannelId}:user{key.UserId.SanitizeForAzureKeys()}"; return TruncateEntityKey(entityKey); case BotStoreType.BotPrivateConversationData: entityKey = $"{key.ChannelId}:private{key.ConversationId.SanitizeForAzureKeys()}:{key.UserId.SanitizeForAzureKeys()}"; return TruncateEntityKey(entityKey); default: throw new ArgumentException("Unsupported bot store type!"); } } private static string TruncateEntityKey(string entityKey) { if (entityKey.Length > MAX_KEY_LENGTH) { var hash = entityKey.GetHashCode().ToString("x"); entityKey = entityKey.Substring(0, MAX_KEY_LENGTH - hash.Length) + hash; } return entityKey; } internal static class StringExtensions { } [JsonProperty(PropertyName = "id")] public string Id { get; set; } [JsonProperty(PropertyName = "botId")] public string BotId { get; set; } [JsonProperty(PropertyName = "channelId")] public string ChannelId { get; set; } [JsonProperty(PropertyName = "conversationId")] public string ConversationId { get; set; } [JsonProperty(PropertyName = "userId")] public string UserId { get; set; } [JsonProperty(PropertyName = "data")] public object Data { get; set; } } } ================================================ FILE: blog-samples/CSharp/BotStateExport/BotStateExport/BotStateExport/Extensions.cs ================================================ using System.Collections.Generic; namespace Microsoft.Bot.Builder.Azure { internal static class StringExtensions { private static readonly Dictionary _DefaultReplacementsForCharactersDisallowedByAzure = new Dictionary() { { "/", "|s|" }, { @"\", "|b|" }, { "#", "|h|" }, { "?", "|q|" } }; internal static string SanitizeForAzureKeys(this string input, Dictionary replacements = null) { var repmap = replacements ?? _DefaultReplacementsForCharactersDisallowedByAzure; return input.Trim().Replace("/", repmap["/"]).Replace(@"\", repmap[@"\"]).Replace("#", repmap["#"]).Replace("?", repmap["?"]); } } } ================================================ FILE: blog-samples/CSharp/BotStateExport/BotStateExport/BotStateExport/Program.cs ================================================ using System; using System.Collections.Generic; using System.IO; using System.Text; using System.Threading; using System.Threading.Tasks; using CommandLine; using Microsoft.Bot.Builder.Dialogs.Internals; using Microsoft.Bot.Connector; using Newtonsoft.Json; namespace ConsoleApp1 { class Options { [Option('a', "appId", Required = true, HelpText = "Microsoft Application Id for bot")] public string AppId { get; set; } [Option('b', "botId", Required = true, HelpText = "bot Id")] public string BotId { get; set; } [Option('c', "connectionString", HelpText = "set Azure Storage table connection string")] public string ConnectionString { get; set; } [Option('d', "destination", HelpText = "set 'cosmos', 'file', or 'table'")] public string Destination { get; set; } [Option('f', "fileName", HelpText = "file in which to write exported data")] public string FileName { get; set; } [Option('k', "key", HelpText = "Cosmos DB key")] public string CosmosDbKey { get; set; } [Option('p', "password", Required = true, HelpText = "Microsoft password for bot")] public string AppPassword { get; set; } [Option('s', "stateUrl", Required = true, HelpText = "Url for state store", Default = "https://connector-api-westus.azurewebsites.net")] public string StateUrl { get; set; } [Option('u', "url", HelpText = "Cosmos DB Url")] public string CosmosDbUrl { get; set; } } class Program { static void Main(string[] args) { bool result; CommandLine.Parser.Default.ParseArguments(args) .WithParsed(opts => result = DoExport(opts).Result) .WithNotParsed((errs) => { foreach (var err in errs) { Console.WriteLine(err.ToString()); }; }); } public static readonly HashSet KnownChannelsIds = new HashSet(new[] { "bing", "cortana", "directline", "email", "facebook", "groupme", "kik", "msteams", "skype", "skypeforbusiness", "slack", "sms", "telegram", "webchat", "wechat" }); public static async Task DoExport(Options opts) { IBotDataStore targetStore = null; StreamWriter outputFile = null; switch (opts.Destination.ToLower()) { case "cosmos": if (string.IsNullOrEmpty(opts.CosmosDbKey) || string.IsNullOrEmpty(opts.CosmosDbUrl)) { Console.WriteLine("Both CosmosDb Key and Url are required in order to copy to a new database"); return false; } try { targetStore = new Microsoft.Bot.Builder.Azure.DocumentDbBotDataStore(new Uri(opts.CosmosDbUrl), opts.CosmosDbKey); } catch (Exception e) { Console.WriteLine($"Problem initializing cosmos DB: {e}"); throw; } break; case "file": if (string.IsNullOrEmpty(opts.FileName)) { Console.WriteLine("FileName must be specified."); return false; } try { outputFile = new StreamWriter(opts.FileName); } catch { Console.WriteLine($"Error creating output file {opts.FileName}."); } outputFile.WriteLine("{\r\n"); break; case "table": if (string.IsNullOrEmpty(opts.ConnectionString)) { Console.WriteLine("ConnectionString must be set for Azure Storage Table"); return false; } try { targetStore = new Microsoft.Bot.Builder.Azure.TableBotDataStore(opts.ConnectionString); } catch (Exception e) { Console.WriteLine($"Problem initializing Azure Storage Table: {e}"); throw; } break; default: Console.WriteLine($"undefined destination type: {opts.Destination}"); break; } if (opts.Destination.ToLower() == "cosmos") { if (string.IsNullOrEmpty(opts.CosmosDbKey) || string.IsNullOrEmpty(opts.CosmosDbUrl)) { Console.WriteLine("Both CosmosDb Key and Url are required in order to copy to a new database"); return false; } try { targetStore = new Microsoft.Bot.Builder.Azure.DocumentDbBotDataStore(new Uri(opts.CosmosDbUrl), opts.CosmosDbKey); } catch (Exception e) { Console.WriteLine($"Problem initializing cosmos DB: {e}"); throw; } } else if (opts.Destination.ToLower() == "table") { if (string.IsNullOrEmpty(opts.ConnectionString) ) { Console.WriteLine("ConnectionString must be set for Azure Storage Table"); return false; } try { targetStore = new Microsoft.Bot.Builder.Azure.TableBotDataStore(opts.ConnectionString); } catch (Exception e) { Console.WriteLine($"Problem initializing Azure Storage Table: {e}"); throw; } } var credentials = new MicrosoftAppCredentials(opts.AppId, opts.AppPassword); var botId =opts.BotId; var stateUrl = new Uri(opts.StateUrl); var serviceUrl = "https://store.botframework.com"; MicrosoftAppCredentials.TrustServiceUrl(stateUrl.AbsoluteUri); string continuationToken = ""; var client = new StateClient(stateUrl, credentials); var state = client.BotState; BotStateDataResult stateResult = null; foreach (var channelId in KnownChannelsIds) { Console.WriteLine($"***{channelId}***"); continuationToken = ""; do { try { // should work with "directline", "facebook", or "kik" stateResult = await BotStateExtensions.ExportBotStateDataAsync(state, channelId, continuationToken).ConfigureAwait(false); foreach (var datum in stateResult.BotStateData) { if ((DateTime.UtcNow - datum.LastModified).HasValue && (DateTime.UtcNow - datum.LastModified).Value.Days < 1) { Console.WriteLine($"LastModified: {datum.LastModified}, UsserId: {datum.UserId}"); } if (datum.Data != "{}") { Console.WriteLine($"conversationID: {datum.ConversationId}\tuserId: {datum.UserId}\tdata:{datum.Data}\n"); if (targetStore != null) { var cancellationToken = new CancellationToken(); var address = new Microsoft.Bot.Builder.Dialogs.Address(botId, channelId, datum.UserId, datum.ConversationId, serviceUrl); var botStoreType = string.IsNullOrEmpty(datum.ConversationId) ? BotStoreType.BotUserData : BotStoreType.BotPrivateConversationData; var botData = new BotData { Data = datum.Data }; await targetStore.SaveAsync(address, botStoreType, botData, cancellationToken); } else if (outputFile != null) { var id = new StringBuilder(); id.Append($"{channelId}:"); if (!string.IsNullOrEmpty(datum.ConversationId)) { id.Append($"private{datum.ConversationId}:"); } id.Append(datum.UserId); var serializedData = JsonConvert.SerializeObject(datum.Data); var outputValue = $"\t{{\r\n\t\"id\": \"{id.ToString()}\",\r\n" + $"\t\t\"botId\": \"{botId}\",\r\n" + $"\t\t\"channelId\": \"{channelId}\",\r\n" + $"\t\t\"conversationId\": \"{datum.ConversationId}\",\r\n" + $"\t\t\"userId\": \"{datum.UserId}\",\r\n" + $"\t\t\"data\": {serializedData}\r\n\t}},\r\n"; outputFile.Write(outputValue); } } } continuationToken = stateResult.ContinuationToken; } catch (Exception e) { var errorException = e as ErrorResponseException; if (errorException?.Body?.Error?.Message?.ToLower() == "channel not configured for bot") { continue; } Console.WriteLine(e); } } while (!string.IsNullOrEmpty(continuationToken)); } Console.Write("Press Enter key to continue:"); Console.Read(); if (outputFile != null) { outputFile.WriteLine("}\r\n"); outputFile.Flush(); outputFile.Close(); } // TODO: Do I need to flush the targetStore? If so, what Address should I use? return true; } } } ================================================ FILE: blog-samples/CSharp/BotStateExport/BotStateExport/BotStateExport/Properties/AssemblyInfo.cs ================================================ using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. [assembly: AssemblyTitle("GDPRExport")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("GDPRExport")] [assembly: AssemblyCopyright("Copyright © 2018")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] // Setting ComVisible to false makes the types in this assembly not visible // to COM components. If you need to access a type in this assembly from // COM, set the ComVisible attribute to true on that type. [assembly: ComVisible(false)] // The following GUID is for the ID of the typelib if this project is exposed to COM [assembly: Guid("da593586-7a94-43d4-8ef1-0474eff6d2e0")] // Version information for an assembly consists of the following four values: // // Major Version // Minor Version // Build Number // Revision // // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("1.0.0.0")] [assembly: AssemblyFileVersion("1.0.0.0")] ================================================ FILE: blog-samples/CSharp/BotStateExport/BotStateExport/BotStateExport/TableBotDataStore.cs ================================================ // // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. // // Microsoft Bot Framework: http://botframework.com // // Bot Builder SDK Github: // https://github.com/Microsoft/BotBuilder // // Copyright (c) Microsoft Corporation // All rights reserved. // // MIT License: // 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. // using System; using System.Collections.Generic; using System.Threading.Tasks; using Microsoft.Bot.Connector; using Newtonsoft.Json; using System.Net; using System.Threading; using System.Web; using Microsoft.Bot.Builder.Dialogs.Internals; using Microsoft.Bot.Builder.Dialogs; using Microsoft.WindowsAzure.Storage.Table; using Microsoft.WindowsAzure.Storage; using System.IO; using System.IO.Compression; using Newtonsoft.Json.Linq; namespace Microsoft.Bot.Builder.Azure { /// /// Implementation using Azure Storage Table /// public class TableBotDataStore : IBotDataStore { private static HashSet checkedTables = new HashSet(); /// /// Creates an instance of the that uses the azure table storage. /// /// The storage connection string. /// The name of table. public TableBotDataStore(string connectionString, string tableName = "botdata") : this(CloudStorageAccount.Parse(connectionString), tableName) { } /// /// Creates an instance of the that uses the azure table storage. /// /// The storage account. /// The name of table. public TableBotDataStore(CloudStorageAccount storageAccount, string tableName = "botdata") { var tableClient = storageAccount.CreateCloudTableClient(); this.Table = tableClient.GetTableReference(tableName); lock (checkedTables) { if (!checkedTables.Contains(tableName)) { this.Table.CreateIfNotExists(); checkedTables.Add(tableName); } } } /// /// Creates an instance of the that uses the azure table storage. /// /// The cloud table. public TableBotDataStore(CloudTable table) { this.Table = table; } /// /// The . /// public CloudTable Table { get; private set; } async Task IBotDataStore.LoadAsync(IAddress key, BotStoreType botStoreType, CancellationToken cancellationToken) { var entityKey = BotDataEntity.GetEntityKey(key, botStoreType); try { var result = await this.Table.ExecuteAsync(TableOperation.Retrieve(entityKey.PartitionKey, entityKey.RowKey)); BotDataEntity entity = (BotDataEntity)result.Result; if (entity == null) // empty record ready to be saved return new BotData(eTag: String.Empty, data: null); // return botdata return new BotData(entity.ETag, entity.GetData()); } catch (StorageException err) { throw new HttpException(err.RequestInformation.HttpStatusCode, err.RequestInformation.HttpStatusMessage); } } async Task IBotDataStore.SaveAsync(IAddress key, BotStoreType botStoreType, BotData botData, CancellationToken cancellationToken) { var entityKey = BotDataEntity.GetEntityKey(key, botStoreType); BotDataEntity entity = new BotDataEntity(key.BotId, key.ChannelId, key.ConversationId, key.UserId, botData.Data) { ETag = botData.ETag }; entity.PartitionKey = entityKey.PartitionKey; entity.RowKey = entityKey.RowKey; try { if (String.IsNullOrEmpty(entity.ETag)) await this.Table.ExecuteAsync(TableOperation.Insert(entity)); else if (entity.ETag == "*") { if (botData.Data != null) await this.Table.ExecuteAsync(TableOperation.InsertOrReplace(entity)); else await this.Table.ExecuteAsync(TableOperation.Delete(entity)); } else { if (botData.Data != null) await this.Table.ExecuteAsync(TableOperation.Replace(entity)); else await this.Table.ExecuteAsync(TableOperation.Delete(entity)); } } catch (StorageException err) { if ((HttpStatusCode)err.RequestInformation.HttpStatusCode == HttpStatusCode.Conflict) throw new HttpException((int)HttpStatusCode.PreconditionFailed, err.RequestInformation.HttpStatusMessage); throw new HttpException(err.RequestInformation.HttpStatusCode, err.RequestInformation.HttpStatusMessage); } } Task IBotDataStore.FlushAsync(IAddress key, CancellationToken cancellationToken) { // Everything is saved. Flush is no-op return Task.FromResult(true); } } internal class EntityKey { public EntityKey(string partition, string row) { PartitionKey = partition; RowKey = row; } public string PartitionKey { get; private set; } public string RowKey { get; private set; } } internal class BotDataEntity : TableEntity { private static readonly JsonSerializerSettings serializationSettings = new JsonSerializerSettings() { Formatting = Formatting.None, NullValueHandling = NullValueHandling.Ignore }; public BotDataEntity() { } internal BotDataEntity(string botId, string channelId, string conversationId, string userId, object data) { this.BotId = botId; this.ChannelId = channelId; this.ConversationId = conversationId; this.UserId = userId; this.Data = Serialize(data); } private byte[] Serialize(object data) { using (var cmpStream = new MemoryStream()) using (var stream = new GZipStream(cmpStream, CompressionMode.Compress)) using (var streamWriter = new StreamWriter(stream)) { var serializedJSon = JsonConvert.SerializeObject(data, serializationSettings); streamWriter.Write(serializedJSon); streamWriter.Close(); stream.Close(); return cmpStream.ToArray(); } } private object Deserialize(byte[] bytes) { using (var stream = new MemoryStream(bytes)) using (var gz = new GZipStream(stream, CompressionMode.Decompress)) using (var streamReader = new StreamReader(gz)) { return JsonConvert.DeserializeObject(streamReader.ReadToEnd()); } } internal static EntityKey GetEntityKey(IAddress key, BotStoreType botStoreType) { switch (botStoreType) { case BotStoreType.BotConversationData: return new EntityKey($"{key.ChannelId}:conversation", key.ConversationId.SanitizeForAzureKeys()); case BotStoreType.BotUserData: return new EntityKey($"{key.ChannelId}:user", key.UserId.SanitizeForAzureKeys()); case BotStoreType.BotPrivateConversationData: return new EntityKey($"{key.ChannelId}:private", $"{key.ConversationId.SanitizeForAzureKeys()}:{key.UserId.SanitizeForAzureKeys()}"); default: throw new ArgumentException("Unsupported bot store type!"); } } internal ObjectT GetData() { return ((JObject)Deserialize(this.Data)).ToObject(); } internal object GetData() { return Deserialize(this.Data); } public string BotId { get; set; } public string ChannelId { get; set; } public string ConversationId { get; set; } public string UserId { get; set; } public byte[] Data { get; set; } } } ================================================ FILE: blog-samples/CSharp/BotStateExport/BotStateExport/BotStateExport/packages.config ================================================  ================================================ FILE: blog-samples/CSharp/BotStateExport/BotStateExport/BotStateExport.sln ================================================  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 VisualStudioVersion = 15.0.27130.2027 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BotStateExport", "BotStateExport\BotStateExport.csproj", "{DA593586-7A94-43D4-8EF1-0474EFF6D2E0}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {DA593586-7A94-43D4-8EF1-0474EFF6D2E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {DA593586-7A94-43D4-8EF1-0474EFF6D2E0}.Debug|Any CPU.Build.0 = Debug|Any CPU {DA593586-7A94-43D4-8EF1-0474EFF6D2E0}.Release|Any CPU.ActiveCfg = Release|Any CPU {DA593586-7A94-43D4-8EF1-0474EFF6D2E0}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {2F9065FE-E24D-4377-AD1D-631E08ED7E57} EndGlobalSection EndGlobal ================================================ FILE: blog-samples/CSharp/BotStateExport/README.md ================================================ This is some rough code for exporting an Azure Bot Service bot's bot state data. This version just dumps it to the console. ================================================ FILE: blog-samples/CSharp/Custom-State-BotBuilder-Azure-Sample/Azure-DocumentDB-Custom-State/App_Start/WebApiConfig.cs ================================================ using Newtonsoft.Json; using Newtonsoft.Json.Serialization; using System; using System.Collections.Generic; using System.Linq; using System.Web.Http; namespace Azure_DocumentDB_Custom_State { public static class WebApiConfig { public static void Register(HttpConfiguration config) { // Json settings config.Formatters.JsonFormatter.SerializerSettings.NullValueHandling = NullValueHandling.Ignore; config.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); config.Formatters.JsonFormatter.SerializerSettings.Formatting = Formatting.Indented; JsonConvert.DefaultSettings = () => new JsonSerializerSettings() { ContractResolver = new CamelCasePropertyNamesContractResolver(), Formatting = Newtonsoft.Json.Formatting.Indented, NullValueHandling = NullValueHandling.Ignore, }; // Web API configuration and services // Web API routes config.MapHttpAttributeRoutes(); config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{id}", defaults: new { id = RouteParameter.Optional } ); } } } ================================================ FILE: blog-samples/CSharp/Custom-State-BotBuilder-Azure-Sample/Azure-DocumentDB-Custom-State/Azure-DocumentDB-Custom-State.csproj ================================================  Debug AnyCPU 2.0 {FD99C7C4-DA5C-4527-B65D-00B29487E66F} {349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} Library Properties Azure_Blob_Custom_State Bot Application v4.6 true true full false bin\ DEBUG;TRACE prompt 4 pdbonly true bin\ TRACE prompt 4 ..\packages\Autofac.4.6.2\lib\net45\Autofac.dll $(SolutionDir)\packages\Chronic.Signed.0.3.2\lib\net40\Chronic.dll ..\packages\EntityFramework.6.2.0\lib\net45\EntityFramework.dll ..\packages\EntityFramework.6.2.0\lib\net45\EntityFramework.SqlServer.dll ..\packages\Microsoft.Azure.DocumentDB.1.19.1\lib\net45\Microsoft.Azure.Documents.Client.dll ..\packages\Microsoft.Azure.KeyVault.Core.2.0.4\lib\net45\Microsoft.Azure.KeyVault.Core.dll ..\packages\Microsoft.Bot.Builder.3.12.2.4\lib\net46\Microsoft.Bot.Builder.dll ..\packages\Microsoft.Bot.Builder.3.12.2.4\lib\net46\Microsoft.Bot.Builder.Autofac.dll ..\packages\Microsoft.Bot.Builder.Azure.3.2.5\lib\net46\Microsoft.Bot.Builder.Azure.dll ..\packages\Microsoft.Bot.Builder.History.3.12.2.4\lib\net46\Microsoft.Bot.Builder.History.dll ..\packages\Microsoft.Bot.Connector.3.12.2.4\lib\net46\Microsoft.Bot.Connector.dll ..\packages\Microsoft.Data.Edm.5.8.3\lib\net40\Microsoft.Data.Edm.dll ..\packages\Microsoft.Data.OData.5.8.3\lib\net40\Microsoft.Data.OData.dll ..\packages\Microsoft.Data.Services.Client.5.8.3\lib\net40\Microsoft.Data.Services.Client.dll ..\packages\Microsoft.IdentityModel.Logging.1.1.5\lib\net451\Microsoft.IdentityModel.Logging.dll $(SolutionDir)\packages\Microsoft.IdentityModel.Protocol.Extensions.1.0.4.403061554\lib\net45\Microsoft.IdentityModel.Protocol.Extensions.dll ..\packages\Microsoft.IdentityModel.Protocols.2.1.5\lib\net451\Microsoft.IdentityModel.Protocols.dll ..\packages\Microsoft.IdentityModel.Protocols.OpenIdConnect.2.1.5\lib\net451\Microsoft.IdentityModel.Protocols.OpenIdConnect.dll ..\packages\Microsoft.IdentityModel.Tokens.5.1.5\lib\net451\Microsoft.IdentityModel.Tokens.dll ..\packages\Microsoft.Rest.ClientRuntime.2.3.10\lib\net452\Microsoft.Rest.ClientRuntime.dll ..\packages\Microsoft.WindowsAzure.ConfigurationManager.3.2.3\lib\net40\Microsoft.WindowsAzure.Configuration.dll ..\packages\WindowsAzure.Storage.8.6.0\lib\net45\Microsoft.WindowsAzure.Storage.dll ..\packages\Newtonsoft.Json.10.0.3\lib\net45\Newtonsoft.Json.dll ..\packages\System.IdentityModel.Tokens.Jwt.5.1.5\lib\net451\System.IdentityModel.Tokens.Jwt.dll $(SolutionDir)\packages\Microsoft.AspNet.WebApi.Client.5.2.3\lib\net45\System.Net.Http.Formatting.dll ..\packages\System.Spatial.5.8.3\lib\net40\System.Spatial.dll $(SolutionDir)\packages\Microsoft.AspNet.WebApi.Core.5.2.3\lib\net45\System.Web.Http.dll $(SolutionDir)\packages\Microsoft.AspNet.WebApi.WebHost.5.2.3\lib\net45\System.Web.Http.WebHost.dll Designer Global.asax Designer Web.config Web.config 10.0 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) true True True 3979 / http://localhost:3979/ False False False This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. ================================================ FILE: blog-samples/CSharp/Custom-State-BotBuilder-Azure-Sample/Azure-DocumentDB-Custom-State/Controllers/MessagesController.cs ================================================ using System.Net; using System.Net.Http; using System.Threading.Tasks; using System.Web.Http; using Microsoft.Bot.Builder.Dialogs; using Microsoft.Bot.Connector; namespace Azure_Blob_Custom_State { [BotAuthentication] public class MessagesController : ApiController { /// /// POST: api/Messages /// Receive a message from a user and reply to it /// public async Task Post([FromBody]Activity activity) { if (activity.Type == ActivityTypes.Message) { await Conversation.SendAsync(activity, () => new Dialogs.RootDialog()); } else { HandleSystemMessage(activity); } var response = Request.CreateResponse(HttpStatusCode.OK); return response; } private Activity HandleSystemMessage(Activity message) { if (message.Type == ActivityTypes.DeleteUserData) { // Implement user deletion here // If we handle user deletion, return a real message } else if (message.Type == ActivityTypes.ConversationUpdate) { // Handle conversation state changes, like members being added and removed // Use Activity.MembersAdded and Activity.MembersRemoved and Activity.Action for info // Not available in all channels } else if (message.Type == ActivityTypes.ContactRelationUpdate) { // Handle add/remove from contact lists // Activity.From + Activity.Action represent what happened } else if (message.Type == ActivityTypes.Typing) { // Handle knowing tha the user is typing } else if (message.Type == ActivityTypes.Ping) { } return null; } } } ================================================ FILE: blog-samples/CSharp/Custom-State-BotBuilder-Azure-Sample/Azure-DocumentDB-Custom-State/Dialogs/RootDialog.cs ================================================ using System; using System.Threading.Tasks; using Microsoft.Bot.Builder.Dialogs; using Microsoft.Bot.Connector; namespace Azure_Blob_Custom_State.Dialogs { [Serializable] public class RootDialog : IDialog { public Task StartAsync(IDialogContext context) { context.Wait(MessageReceivedAsync); return Task.CompletedTask; } private async Task MessageReceivedAsync(IDialogContext context, IAwaitable result) { var activity = await result as Activity; await context.PostAsync($"You said {activity.Text}"); context.Wait(MessageReceivedAsync); } } } ================================================ FILE: blog-samples/CSharp/Custom-State-BotBuilder-Azure-Sample/Azure-DocumentDB-Custom-State/Global.asax ================================================ <%@ Application Codebehind="Global.asax.cs" Inherits="Azure_Blob_Custom_State.WebApiApplication" Language="C#" %> ================================================ FILE: blog-samples/CSharp/Custom-State-BotBuilder-Azure-Sample/Azure-DocumentDB-Custom-State/Global.asax.cs ================================================ using System.Reflection; using System.Web.Http; using Autofac; using Microsoft.Bot.Builder.Azure; using Microsoft.Bot.Builder.Dialogs; using Microsoft.Bot.Builder.Dialogs.Internals; using Microsoft.Bot.Connector; using System.Configuration; using System; namespace Azure_DocumentDB_Custom_State { public class WebApiApplication : System.Web.HttpApplication { protected void Application_Start() { Conversation.UpdateContainer( builder => { builder.RegisterModule(new AzureModule(Assembly.GetExecutingAssembly())); // Bot Storage: register state storage for your bot // Default store: volatile in-memory store - Only for prototyping! // var store = new InMemoryDataStore(); var uri = new Uri(ConfigurationManager.AppSettings["DocumentDBUri"]); var key = ConfigurationManager.AppSettings["DocumentDBKey"]; var store = new DocumentDbBotDataStore(uri, key); builder.Register(c => store) .Keyed>(AzureModule.Key_DataStore) .AsSelf() .SingleInstance(); }); GlobalConfiguration.Configure(WebApiConfig.Register); } } } ================================================ FILE: blog-samples/CSharp/Custom-State-BotBuilder-Azure-Sample/Azure-DocumentDB-Custom-State/Properties/AssemblyInfo.cs ================================================ using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. [assembly: AssemblyTitle("Azure_Blob_Custom_State")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("Azure_Blob_Custom_State")] [assembly: AssemblyCopyright("Copyright © 2016")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] // Setting ComVisible to false makes the types in this assembly not visible // to COM components. If you need to access a type in this assembly from // COM, set the ComVisible attribute to true on that type. [assembly: ComVisible(false)] // The following GUID is for the ID of the typelib if this project is exposed to COM [assembly: Guid("fd99c7c4-da5c-4527-b65d-00b29487e66f")] // Version information for an assembly consists of the following four values: // // Major Version // Minor Version // Build Number // Revision // // You can specify all the values or you can default the Revision and Build Numbers // by using the '*' as shown below: [assembly: AssemblyVersion("1.0.0.0")] [assembly: AssemblyFileVersion("1.0.0.0")] ================================================ FILE: blog-samples/CSharp/Custom-State-BotBuilder-Azure-Sample/Azure-DocumentDB-Custom-State/README.md ================================================ # Azure DocumentDB Sample This simple echo bots illustrates how to use your own Azure Table Storage to store the bot state. To use Azure Table Store, we configure the Autofac Dependency Injection in [Global.asax](Global.asax.cs). Particularly the following is the piece of code that configures injection of Azure DocumentDB (CosmosDB) Storage: >Note: DocumentDB was rebranded as CosmosDB - [see here](https://buildazure.com/2017/05/10/cosmosdb-the-new-documentdb-nosql-database-in-microsoft-azure/) for details ```csharp var store = new TableBotDataStore(ConfigurationManager.ConnectionStrings["StorageConnectionString"].ConnectionString); builder.Register(c => store) .Keyed>(AzureModule.Key_DataStore) .AsSelf() .SingleInstance(); ``` ## References - Documentation - [State Data for Bots in .NET](https://docs.microsoft.com/en-us/bot-framework/dotnet/bot-builder-dotnet-state) - Source code for [BotBuilder-Azure on GitHub](https://github.com/Microsoft/BotBuilder-Azure) - Nuget packaget for .NET [Microsoft.Bot.Builder.Azure](https://www.nuget.org/packages/Microsoft.Bot.Builder.Azure/) - Get started with [Azure DocumentDB](https://docs.microsoft.com/en-us/azure/cosmos-db/create-documentdb-dotnet) ================================================ FILE: blog-samples/CSharp/Custom-State-BotBuilder-Azure-Sample/Azure-DocumentDB-Custom-State/Web.Debug.config ================================================ ================================================ FILE: blog-samples/CSharp/Custom-State-BotBuilder-Azure-Sample/Azure-DocumentDB-Custom-State/Web.Release.config ================================================ ================================================ FILE: blog-samples/CSharp/Custom-State-BotBuilder-Azure-Sample/Azure-DocumentDB-Custom-State/Web.config ================================================ 
================================================ FILE: blog-samples/CSharp/Custom-State-BotBuilder-Azure-Sample/Azure-DocumentDB-Custom-State/default.htm ================================================ 

Azure_Blob_Custom_State

Describe your bot here and your terms of use etc.

Visit Bot Framework to register your bot. When you register it, remember to set your bot's endpoint to

https://your_bots_hostname/api/messages

================================================ FILE: blog-samples/CSharp/Custom-State-BotBuilder-Azure-Sample/Azure-DocumentDB-Custom-State/packages.config ================================================  ================================================ FILE: blog-samples/CSharp/Custom-State-BotBuilder-Azure-Sample/Azure-Table-Custom-State/App_Start/WebApiConfig.cs ================================================ using Newtonsoft.Json; using Newtonsoft.Json.Serialization; using System; using System.Collections.Generic; using System.Linq; using System.Web.Http; namespace Azure_Table_Custom_State { public static class WebApiConfig { public static void Register(HttpConfiguration config) { // Json settings config.Formatters.JsonFormatter.SerializerSettings.NullValueHandling = NullValueHandling.Ignore; config.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); config.Formatters.JsonFormatter.SerializerSettings.Formatting = Formatting.Indented; JsonConvert.DefaultSettings = () => new JsonSerializerSettings() { ContractResolver = new CamelCasePropertyNamesContractResolver(), Formatting = Newtonsoft.Json.Formatting.Indented, NullValueHandling = NullValueHandling.Ignore, }; // Web API configuration and services // Web API routes config.MapHttpAttributeRoutes(); config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{id}", defaults: new { id = RouteParameter.Optional } ); } } } ================================================ FILE: blog-samples/CSharp/Custom-State-BotBuilder-Azure-Sample/Azure-Table-Custom-State/Azure-Table-Custom-State.csproj ================================================  Debug AnyCPU 2.0 {B665E69D-98E2-47A7-AF4D-3EAA9D8E1439} {349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} Library Properties Custom_State_Sample Bot Application v4.6 true true full false bin\ DEBUG;TRACE prompt 4 pdbonly true bin\ TRACE prompt 4 ..\packages\Autofac.4.6.2\lib\net45\Autofac.dll $(SolutionDir)\packages\Chronic.Signed.0.3.2\lib\net40\Chronic.dll ..\packages\EntityFramework.6.2.0\lib\net45\EntityFramework.dll ..\packages\EntityFramework.6.2.0\lib\net45\EntityFramework.SqlServer.dll ..\packages\Microsoft.Azure.DocumentDB.1.19.1\lib\net45\Microsoft.Azure.Documents.Client.dll ..\packages\Microsoft.Azure.KeyVault.Core.2.0.4\lib\net45\Microsoft.Azure.KeyVault.Core.dll ..\packages\Microsoft.Bot.Builder.3.12.2.4\lib\net46\Microsoft.Bot.Builder.dll ..\packages\Microsoft.Bot.Builder.3.12.2.4\lib\net46\Microsoft.Bot.Builder.Autofac.dll ..\packages\Microsoft.Bot.Builder.Azure.3.2.5\lib\net46\Microsoft.Bot.Builder.Azure.dll ..\packages\Microsoft.Bot.Builder.History.3.12.2.4\lib\net46\Microsoft.Bot.Builder.History.dll ..\packages\Microsoft.Bot.Connector.3.12.2.4\lib\net46\Microsoft.Bot.Connector.dll ..\packages\Microsoft.Data.Edm.5.8.3\lib\net40\Microsoft.Data.Edm.dll ..\packages\Microsoft.Data.OData.5.8.3\lib\net40\Microsoft.Data.OData.dll ..\packages\Microsoft.Data.Services.Client.5.8.3\lib\net40\Microsoft.Data.Services.Client.dll ..\packages\Microsoft.IdentityModel.Logging.1.1.5\lib\net451\Microsoft.IdentityModel.Logging.dll $(SolutionDir)\packages\Microsoft.IdentityModel.Protocol.Extensions.1.0.4.403061554\lib\net45\Microsoft.IdentityModel.Protocol.Extensions.dll ..\packages\Microsoft.IdentityModel.Protocols.2.1.5\lib\net451\Microsoft.IdentityModel.Protocols.dll ..\packages\Microsoft.IdentityModel.Protocols.OpenIdConnect.2.1.5\lib\net451\Microsoft.IdentityModel.Protocols.OpenIdConnect.dll ..\packages\Microsoft.IdentityModel.Tokens.5.1.5\lib\net451\Microsoft.IdentityModel.Tokens.dll ..\packages\Microsoft.Rest.ClientRuntime.2.3.10\lib\net452\Microsoft.Rest.ClientRuntime.dll ..\packages\Microsoft.WindowsAzure.ConfigurationManager.3.2.3\lib\net40\Microsoft.WindowsAzure.Configuration.dll ..\packages\WindowsAzure.Storage.8.6.0\lib\net45\Microsoft.WindowsAzure.Storage.dll ..\packages\Newtonsoft.Json.10.0.3\lib\net45\Newtonsoft.Json.dll ..\packages\System.IdentityModel.Tokens.Jwt.5.1.5\lib\net451\System.IdentityModel.Tokens.Jwt.dll $(SolutionDir)\packages\Microsoft.AspNet.WebApi.Client.5.2.3\lib\net45\System.Net.Http.Formatting.dll ..\packages\System.Spatial.5.8.3\lib\net40\System.Spatial.dll $(SolutionDir)\packages\Microsoft.AspNet.WebApi.Core.5.2.3\lib\net45\System.Web.Http.dll $(SolutionDir)\packages\Microsoft.AspNet.WebApi.WebHost.5.2.3\lib\net45\System.Web.Http.WebHost.dll Designer Global.asax Designer Web.config Web.config 10.0 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) true True True 3979 / http://localhost:3979/ False False False This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. ================================================ FILE: blog-samples/CSharp/Custom-State-BotBuilder-Azure-Sample/Azure-Table-Custom-State/Controllers/MessagesController.cs ================================================ using System.Net; using System.Net.Http; using System.Threading.Tasks; using System.Web.Http; using Microsoft.Bot.Builder.Dialogs; using Microsoft.Bot.Connector; namespace Custom_State_Sample { [BotAuthentication] public class MessagesController : ApiController { /// /// POST: api/Messages /// Receive a message from a user and reply to it /// public async Task Post([FromBody]Activity activity) { if (activity.Type == ActivityTypes.Message) { await Conversation.SendAsync(activity, () => new Dialogs.RootDialog()); } else { HandleSystemMessage(activity); } var response = Request.CreateResponse(HttpStatusCode.OK); return response; } private Activity HandleSystemMessage(Activity message) { if (message.Type == ActivityTypes.DeleteUserData) { // Implement user deletion here // If we handle user deletion, return a real message } else if (message.Type == ActivityTypes.ConversationUpdate) { // Handle conversation state changes, like members being added and removed // Use Activity.MembersAdded and Activity.MembersRemoved and Activity.Action for info // Not available in all channels } else if (message.Type == ActivityTypes.ContactRelationUpdate) { // Handle add/remove from contact lists // Activity.From + Activity.Action represent what happened } else if (message.Type == ActivityTypes.Typing) { // Handle knowing tha the user is typing } else if (message.Type == ActivityTypes.Ping) { } return null; } } } ================================================ FILE: blog-samples/CSharp/Custom-State-BotBuilder-Azure-Sample/Azure-Table-Custom-State/Dialogs/RootDialog.cs ================================================ using System; using System.Threading.Tasks; using Microsoft.Bot.Builder.Dialogs; using Microsoft.Bot.Connector; namespace Custom_State_Sample.Dialogs { [Serializable] public class RootDialog : IDialog { public Task StartAsync(IDialogContext context) { context.Wait(MessageReceivedAsync); return Task.CompletedTask; } private async Task MessageReceivedAsync(IDialogContext context, IAwaitable result) { var activity = await result as Activity; await context.PostAsync($"You said {activity.Text}"); context.Wait(MessageReceivedAsync); } } } ================================================ FILE: blog-samples/CSharp/Custom-State-BotBuilder-Azure-Sample/Azure-Table-Custom-State/Global.asax ================================================ <%@ Application Codebehind="Global.asax.cs" Inherits="Custom_State_Sample.WebApiApplication" Language="C#" %> ================================================ FILE: blog-samples/CSharp/Custom-State-BotBuilder-Azure-Sample/Azure-Table-Custom-State/Global.asax.cs ================================================ using System.Reflection; using System.Web.Http; using Autofac; using Microsoft.Bot.Builder.Azure; using Microsoft.Bot.Builder.Dialogs; using Microsoft.Bot.Builder.Dialogs.Internals; using Microsoft.Bot.Connector; using System.Configuration; namespace Azure_Table_Custom_State { public class WebApiApplication : System.Web.HttpApplication { protected void Application_Start() { Conversation.UpdateContainer( builder => { builder.RegisterModule(new AzureModule(Assembly.GetExecutingAssembly())); // Bot Storage: register state storage for your bot // Default store: volatile in-memory store - Only for prototyping! // var store = new InMemoryDataStore(); // This sample will use Azure Table var store = new TableBotDataStore(ConfigurationManager.ConnectionStrings["StorageConnectionString"].ConnectionString); builder.Register(c => store) .Keyed>(AzureModule.Key_DataStore) .AsSelf() .SingleInstance(); }); GlobalConfiguration.Configure(WebApiConfig.Register); } } } ================================================ FILE: blog-samples/CSharp/Custom-State-BotBuilder-Azure-Sample/Azure-Table-Custom-State/Properties/AssemblyInfo.cs ================================================ using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. [assembly: AssemblyTitle("Custom_State_Sample")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("Custom_State_Sample")] [assembly: AssemblyCopyright("Copyright © 2016")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] // Setting ComVisible to false makes the types in this assembly not visible // to COM components. If you need to access a type in this assembly from // COM, set the ComVisible attribute to true on that type. [assembly: ComVisible(false)] // The following GUID is for the ID of the typelib if this project is exposed to COM [assembly: Guid("b665e69d-98e2-47a7-af4d-3eaa9d8e1439")] // Version information for an assembly consists of the following four values: // // Major Version // Minor Version // Build Number // Revision // // You can specify all the values or you can default the Revision and Build Numbers // by using the '*' as shown below: [assembly: AssemblyVersion("1.0.0.0")] [assembly: AssemblyFileVersion("1.0.0.0")] ================================================ FILE: blog-samples/CSharp/Custom-State-BotBuilder-Azure-Sample/Azure-Table-Custom-State/README.md ================================================ # Azure Table Sample This simple echo bots illustrates how to use your own Azure Table Storage to store the bot state. To use Azure Table Store, we configure the Autofac Dependency Injection in [Global.asax](Global.asax.cs). Particularly the following is the piece of code that configures injection of Azure Table Storage: ```csharp var store = new TableBotDataStore(ConfigurationManager.ConnectionStrings["StorageConnectionString"].ConnectionString); builder.Register(c => store) .Keyed>(AzureModule.Key_DataStore) .AsSelf() .SingleInstance(); ``` ## References - Documentation - [State Data for Bots in .NET](https://docs.microsoft.com/en-us/bot-framework/dotnet/bot-builder-dotnet-state) - Source code for [BotBuilder-Azure on GitHub](https://github.com/Microsoft/BotBuilder-Azure) - Nuget packaget for .NET [Microsoft.Bot.Builder.Azure](https://www.nuget.org/packages/Microsoft.Bot.Builder.Azure/) - Get started with [Azure Table Storage](https://docs.microsoft.com/en-us/azure/cosmos-db/table-storage-how-to-use-dotnet) ================================================ FILE: blog-samples/CSharp/Custom-State-BotBuilder-Azure-Sample/Azure-Table-Custom-State/Web.Debug.config ================================================ ================================================ FILE: blog-samples/CSharp/Custom-State-BotBuilder-Azure-Sample/Azure-Table-Custom-State/Web.Release.config ================================================ ================================================ FILE: blog-samples/CSharp/Custom-State-BotBuilder-Azure-Sample/Azure-Table-Custom-State/Web.config ================================================ 
================================================ FILE: blog-samples/CSharp/Custom-State-BotBuilder-Azure-Sample/Azure-Table-Custom-State/default.htm ================================================ 

Custom_State_Sample

Describe your bot here and your terms of use etc.

Visit Bot Framework to register your bot. When you register it, remember to set your bot's endpoint to

https://your_bots_hostname/api/messages

================================================ FILE: blog-samples/CSharp/Custom-State-BotBuilder-Azure-Sample/Azure-Table-Custom-State/packages.config ================================================  ================================================ FILE: blog-samples/CSharp/Custom-State-BotBuilder-Azure-Sample/Custom-State-Sample.sln ================================================  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 VisualStudioVersion = 15.0.27004.2002 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Azure-Table-Custom-State", "Custom-State-Sample\Azure-Table-Custom-State.csproj", "{B665E69D-98E2-47A7-AF4D-3EAA9D8E1439}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Azure-DocumentDB-Custom-State", "Azure-Blob-Custom-State\Azure-DocumentDB-Custom-State.csproj", "{FD99C7C4-DA5C-4527-B65D-00B29487E66F}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {B665E69D-98E2-47A7-AF4D-3EAA9D8E1439}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B665E69D-98E2-47A7-AF4D-3EAA9D8E1439}.Debug|Any CPU.Build.0 = Debug|Any CPU {B665E69D-98E2-47A7-AF4D-3EAA9D8E1439}.Release|Any CPU.ActiveCfg = Release|Any CPU {B665E69D-98E2-47A7-AF4D-3EAA9D8E1439}.Release|Any CPU.Build.0 = Release|Any CPU {FD99C7C4-DA5C-4527-B65D-00B29487E66F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {FD99C7C4-DA5C-4527-B65D-00B29487E66F}.Debug|Any CPU.Build.0 = Debug|Any CPU {FD99C7C4-DA5C-4527-B65D-00B29487E66F}.Release|Any CPU.ActiveCfg = Release|Any CPU {FD99C7C4-DA5C-4527-B65D-00B29487E66F}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {9593BDCF-32D9-4BA4-9BFD-BF7E43B74DCF} EndGlobalSection EndGlobal ================================================ FILE: blog-samples/CSharp/Custom-State-BotBuilder-Azure-Sample/README.md ================================================ # Custom state data for your Bots The [Bot Framework State Service](https://docs.microsoft.com/en-us/bot-framework/dotnet/bot-builder-dotnet-state) API is not recommended for production environments. Currently, every bot built with the SDK comes with this setting by default, but it is only meant for prototyping. These samples are simple echo bots which leverage the [Microsoft.Bot.Builder.Azure](https://www.nuget.org/packages/Microsoft.Bot.Builder.Azure/) Nuget package to easily create a custom state data store for your bots. Currently, the package supports seamless integration with [Azure Table storage](https://docs.microsoft.com/en-us/azure/cosmos-db/table-storage-how-to-use-dotnet) and [Azure DocumentDB](https://docs.microsoft.com/en-us/azure/cosmos-db/create-documentdb-dotnet). Creating a custom state store for your bot provides several benefits: - Improved latency for your Bot - Direct control over your bot's state data, which includes information about your users, conversation state, and conversation context. ================================================ FILE: blog-samples/CSharp/FacebookHandover/FacebookHandover.sln ================================================  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.28803.452 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Primary", "Primary\Primary.csproj", "{065B80D0-7968-4E61-B0F6-D04165912FF6}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Secondary", "Secondary\Secondary.csproj", "{57786F2D-4C81-4FDF-9011-55652A78DF0B}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {065B80D0-7968-4E61-B0F6-D04165912FF6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {065B80D0-7968-4E61-B0F6-D04165912FF6}.Debug|Any CPU.Build.0 = Debug|Any CPU {065B80D0-7968-4E61-B0F6-D04165912FF6}.Release|Any CPU.ActiveCfg = Release|Any CPU {065B80D0-7968-4E61-B0F6-D04165912FF6}.Release|Any CPU.Build.0 = Release|Any CPU {57786F2D-4C81-4FDF-9011-55652A78DF0B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {57786F2D-4C81-4FDF-9011-55652A78DF0B}.Debug|Any CPU.Build.0 = Debug|Any CPU {57786F2D-4C81-4FDF-9011-55652A78DF0B}.Release|Any CPU.ActiveCfg = Release|Any CPU {57786F2D-4C81-4FDF-9011-55652A78DF0B}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {6876B9B2-5C7C-41B7-A624-EFB8D070BD4A} EndGlobalSection EndGlobal ================================================ FILE: blog-samples/CSharp/FacebookHandover/FacebookModel/FacebookPassThreadControl.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Newtonsoft.Json; namespace FacebookModel { /// /// A Facebook thread control message, including appid of the new thread owner and an optional message to sent with the request /// /// public class FacebookPassThreadControl { /// /// The app id of the new owner. /// /// /// 263902037430900 for the page inbox. /// [JsonProperty("new_owner_app_id")] public string NewOwnerAppId; /// /// Message sent from the requester. /// /// /// Example: "i want the control!" /// [JsonProperty("metadata")] public string Metadata; } } ================================================ FILE: blog-samples/CSharp/FacebookHandover/FacebookModel/FacebookPayload.cs ================================================ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. using Newtonsoft.Json; namespace FacebookModel { /// /// Simple version of the payload received from the Facebook channel. /// public class FacebookPayload { /// /// Gets or sets the sender of the message. /// [JsonProperty("sender")] public FacebookPsid Sender { get; set; } /// /// Gets or sets the recipient of the message. /// [JsonProperty("recipient")] public FacebookPsid Recipient { get; set; } /// /// Gets or sets the request_thread_control of the control request. /// [JsonProperty("request_thread_control")] public FacebookRequestThreadControl RequestThreadControl; /// /// Gets or sets the pass_thread_control of the control request. /// [JsonProperty("pass_thread_control")] public FacebookPassThreadControl PassThreadControl; /// /// Gets or sets the take_thread_control of the control request. /// [JsonProperty("take_thread_control")] public FacebookTakeThreadControl TakeThreadControl; } } ================================================ FILE: blog-samples/CSharp/FacebookHandover/FacebookModel/FacebookPsid.cs ================================================ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. using Newtonsoft.Json; namespace FacebookModel { /// /// Defines a Facebook PSID. /// public class FacebookPsid { /// /// A Facebook page-scoped ID. /// [JsonProperty("id")] public string Id { get; set; } } } ================================================ FILE: blog-samples/CSharp/FacebookHandover/FacebookModel/FacebookRequestThreadControl.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Newtonsoft.Json; namespace FacebookModel { /// /// A Facebook thread control message, including appid of requested thread owner and an optional message to send with the request /// /// public class FacebookRequestThreadControl { /// /// The app id of the requested owner. /// /// /// 263902037430900 for the page inbox. /// [JsonProperty("requested_owner_app_id")] public string RequestedOwnerAppId; // 263902037430900 for page /// /// Message sent from the requester. /// /// /// Example: "i want the control!" /// [JsonProperty("metadata")] public string Metadata; } } ================================================ FILE: blog-samples/CSharp/FacebookHandover/FacebookModel/FacebookStandby.cs ================================================ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. using Newtonsoft.Json; namespace FacebookModel { /// /// A Facebook stanby event payload definition. /// /// See messaging standby Facebook documentation /// for more information on standby. public class FacebookStandbys { [JsonProperty("id")] public string Id; [JsonProperty("time")] public long Time; [JsonProperty("standBy")] public FacebookStandby[] Standbys; } public class FacebookStandby { [JsonProperty("sender")] public FacebookPsid Sender; [JsonProperty("recipient")] public FacebookPsid Recipient; [JsonProperty("timestamp")] public long Timestamp; [JsonProperty("message")] public FacebookStandByMessage Message; } public class FacebookStandByMessage { [JsonProperty("mid")] public string MId; [JsonProperty("seq")] public long Seq; [JsonProperty("text")] public string Text; } } ================================================ FILE: blog-samples/CSharp/FacebookHandover/FacebookModel/FacebookTakeThreadControl.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Newtonsoft.Json; namespace FacebookModel { /// /// A Facebook thread control message, including appid of the previous thread owner and an optional message sent with the request /// /// public class FacebookTakeThreadControl { /// /// The app id of the previous owner. /// /// /// 263902037430900 for the page inbox. /// [JsonProperty("previous_owner_app_id")] public string PreviousOwnerAppId; /// /// Message sent from the requester. /// /// /// Example: "All yours!" /// [JsonProperty("metadata")] public string Metadata; } } ================================================ FILE: blog-samples/CSharp/FacebookHandover/FacebookModel/FacebookThreadControlHelper.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Net.Http; using System.Text; using System.Threading; using System.Threading.Tasks; using Microsoft.Bot.Builder; using Microsoft.Bot.Schema; using Newtonsoft.Json; using Newtonsoft.Json.Linq; namespace FacebookModel { public static class FacebookThreadControlHelper { public const string GRAPH_API_BASE_URL = "https://graph.facebook.com/v3.3/me/{0}?access_token={1}"; private static readonly HttpClient _httpClient = new HttpClient(); private static async Task PostToFacebookAPIAsync(string postType, string pageToken, string content) { var requestPath = string.Format(GRAPH_API_BASE_URL, postType, pageToken); var stringContent = new StringContent(content, Encoding.UTF8, "application/json"); // Create HTTP transport objects using (var requestMessage = new HttpRequestMessage()) { requestMessage.Method = new HttpMethod("POST"); requestMessage.RequestUri = new Uri(requestPath); requestMessage.Content = stringContent; requestMessage.Content.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json; charset=utf-8"); // Make the Http call using (var response = await _httpClient.SendAsync(requestMessage, CancellationToken.None).ConfigureAwait(false)) { // Return true if the call was successfull Debug.Print(await response.Content.ReadAsStringAsync().ConfigureAwait(false)); return response.IsSuccessStatusCode; } } } public static async Task> GetSecondaryReceiversAsync(string pageToken) { var requestPath = string.Format(GRAPH_API_BASE_URL, "secondary_receivers", pageToken); // Create HTTP transport objects using (var requestMessage = new HttpRequestMessage()) { requestMessage.Method = new HttpMethod("GET"); requestMessage.RequestUri = new Uri(requestPath); // Make the Http call using (var response = await _httpClient.SendAsync(requestMessage, CancellationToken.None).ConfigureAwait(false)) { // Interpret response var responseString = await response.Content.ReadAsStringAsync().ConfigureAwait(false); var responseObject = JObject.Parse(responseString); var responseData = responseObject["data"] as JArray; return responseData.Select(receiver => receiver["id"].ToString()).ToList(); } } } public static async Task RequestThreadControlAsync(string pageToken, string userId, string message) { var content = new { recipient = new { id = userId }, metadata = message }; return await PostToFacebookAPIAsync("request_thread_control", pageToken, JsonConvert.SerializeObject(content)).ConfigureAwait(false); } public static async Task TakeThreadControlAsync(string pageToken, string userId, string message) { var content = new { recipient = new { id = userId }, metadata = message }; return await PostToFacebookAPIAsync("take_thread_control", pageToken, JsonConvert.SerializeObject(content)).ConfigureAwait(false); } public static async Task PassThreadControlAsync(string pageToken, string targetAppId, string userId, string message) { var content = new { recipient = new { id = userId }, target_app_id = targetAppId, metadata = message }; return await PostToFacebookAPIAsync("pass_thread_control", pageToken, JsonConvert.SerializeObject(content)).ConfigureAwait(false); } /// /// This extension method populates a turn context's activity with conversation and user information from a Facebook payload. /// This is necessary because a turn context needs that information to send messages to a conversation, /// and event activities don't necessarily come with that information already in place. /// public static void ApplyFacebookPayload(this ITurnContext turnContext, FacebookPayload facebookPayload) { var userId = facebookPayload.Sender.Id; var pageId = facebookPayload.Recipient.Id; var conversationId = string.Format("{0}-{1}", userId, pageId); turnContext.Activity.From = new ChannelAccount(userId); turnContext.Activity.Recipient = new ChannelAccount(pageId); turnContext.Activity.Conversation = new ConversationAccount(id: conversationId); } } } ================================================ FILE: blog-samples/CSharp/FacebookHandover/Primary/Bots/PrimaryBot.cs ================================================ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. // // Generated with Bot Builder V4 SDK Template for Visual Studio EchoBot v4.3.0 using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.Bot.Builder; using Microsoft.Bot.Builder.Dialogs.Choices; using Microsoft.Bot.Schema; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using Newtonsoft.Json.Linq; using FacebookModel; using System; using Newtonsoft.Json; using Microsoft.Bot.Connector; namespace Primary.Bots { /// /// This is based on Eric Dahlvang's FacebookEventsBotExpanded: https://github.com/EricDahlvang/FacebookEventsBotExpanded /// public class PrimaryBot : ActivityHandler { /// /// This option passes thread control from the primary receiver to the page inbox. /// private const string OPTION_PASS_PAGE_INBOX = "Pass to page inbox"; /// /// This option passes thread control from the primary receiver to a secondary receiver. /// private const string OPTION_PASS_SECONDARY_BOT = "Pass to secondary"; /// /// This option is ignored by this bot. /// The secondary bot is meant to be listening for this phrase as a standby event /// and respond to it by requesting thread control. /// private const string OPTION_REQUEST_THREAD_CONTROL = "Receive request"; /// /// This option is ignored by this bot. /// The secondary bot is meant to be listening for this phrase as a standby event /// and respond to it by requesting thread control with "polite" metadata. /// private const string OPTION_REQUEST_THREAD_CONTROL_NICELY = "Receive nice request"; /// /// This is not an option for this bot, /// but this bot is meant to recognize the phrase in a standby event while the secondary bot has thread control /// and respond to it by taking thread control from the secondary bot. /// private const string OPTION_TAKE_THREAD_CONTROL = "Have control taken"; /// /// The constant ID representing the page inbox /// private const string PAGE_INBOX_ID = "263902037430900"; private static readonly List _options = new[] { OPTION_PASS_PAGE_INBOX, OPTION_PASS_SECONDARY_BOT, OPTION_REQUEST_THREAD_CONTROL, OPTION_REQUEST_THREAD_CONTROL_NICELY, }.Select(option => new Choice(option)).ToList(); private readonly ILogger _logger; private readonly IConfiguration _configuration; public PrimaryBot(ILogger logger, IConfiguration configuration) { _logger = logger; _configuration = configuration; } protected override async Task OnMessageActivityAsync(ITurnContext turnContext, CancellationToken cancellationToken) { var text = turnContext.Activity.Text; switch (text) { case OPTION_PASS_PAGE_INBOX: await turnContext.SendActivityAsync("Primary Bot: Passing thread control to the page inbox..."); await FacebookThreadControlHelper.PassThreadControlAsync(_configuration["FacebookPageToken"], PAGE_INBOX_ID, turnContext.Activity.From.Id, text); break; case OPTION_PASS_SECONDARY_BOT: var secondaryReceivers = await FacebookThreadControlHelper.GetSecondaryReceiversAsync(_configuration["FacebookPageToken"]); foreach (var receiver in secondaryReceivers) { if (receiver != PAGE_INBOX_ID) { await turnContext.SendActivityAsync($"Primary Bot: Passing thread control to {receiver}..."); await FacebookThreadControlHelper.PassThreadControlAsync(_configuration["FacebookPageToken"], receiver, turnContext.Activity.From.Id, text); break; } } break; case OPTION_REQUEST_THREAD_CONTROL: case OPTION_REQUEST_THREAD_CONTROL_NICELY: // Do nothing because the secondary receiver should react to these instead break; default: await ShowChoices(turnContext, cancellationToken); break; } } protected override async Task OnConversationUpdateActivityAsync(ITurnContext turnContext, CancellationToken cancellationToken) { _logger.LogInformation("PrimaryBot - Processing a ConversationUpdate Activity."); var facebookPayload = (turnContext.Activity.ChannelData as JObject)?.ToObject(); if (facebookPayload != null) { if (facebookPayload.PassThreadControl != null) { await turnContext.SendActivityAsync($"Primary Bot: Thread control is now passed to {facebookPayload.PassThreadControl.NewOwnerAppId} with the message \"{facebookPayload.PassThreadControl.Metadata}\""); await ShowChoices(turnContext, cancellationToken); } } await base.OnConversationUpdateActivityAsync(turnContext, cancellationToken); } protected override async Task OnEventActivityAsync(ITurnContext turnContext, CancellationToken cancellationToken) { _logger.LogInformation("PrimaryBot - Processing an Event Activity."); // Analyze Facebook payload from EventActivity.Value await ProcessFacebookMessage(turnContext, turnContext.Activity.Value, cancellationToken); } private async Task ProcessFacebookMessage(ITurnContext turnContext, object data, CancellationToken cancellationToken) { return await ProcessStandbyPayload(turnContext, data, cancellationToken) || await ProcessFacebookPayload(turnContext, data, cancellationToken); } private async Task ProcessStandbyPayload(ITurnContext turnContext, object data, CancellationToken cancellationToken) { if (turnContext.Activity.Name?.Equals("standby", StringComparison.InvariantCultureIgnoreCase) == true) { var standbys = (data as JObject)?.ToObject(); if (standbys != null) { foreach (var standby in standbys.Standbys) { await OnFacebookStandby(turnContext, standby, cancellationToken); } return true; } } return false; } protected virtual async Task OnFacebookStandby(ITurnContext turnContext, FacebookStandby facebookStandby, CancellationToken cancellationToken) { _logger.LogInformation("PrimaryBot - Standby message received."); var text = facebookStandby?.Message?.Text; if (text?.Equals(OPTION_TAKE_THREAD_CONTROL, StringComparison.InvariantCultureIgnoreCase) == true) { await FacebookThreadControlHelper.TakeThreadControlAsync(_configuration["FacebookPageToken"], facebookStandby.Sender.Id, text); } } private async Task ProcessFacebookPayload(ITurnContext turnContext, object data, CancellationToken cancellationToken) { try { var facebookPayload = (data as JObject)?.ToObject(); if (facebookPayload != null) { // At this point we know we are on Facebook channel, and can consume the Facebook custom payload // present in channelData. turnContext.ApplyFacebookPayload(facebookPayload); // Thread Control Request if (facebookPayload.RequestThreadControl != null) { await OnFacebookThreadControlRequest(turnContext, facebookPayload, cancellationToken); return true; } } } catch (JsonSerializationException) { if (turnContext.Activity.ChannelId != Channels.Facebook) { await turnContext.SendActivityAsync("Primary Bot: This sample is intended to be used with a Facebook bot."); } } return false; } protected virtual async Task OnFacebookThreadControlRequest(ITurnContext turnContext, FacebookPayload facebookPayload, CancellationToken cancellationToken) { _logger.LogInformation("PrimaryBot - Thread Control Request message received."); string requestedOwnerAppId = facebookPayload.RequestThreadControl.RequestedOwnerAppId; if (facebookPayload.RequestThreadControl.Metadata == "please") { await turnContext.SendActivityAsync($"Primary Bot: {requestedOwnerAppId} requested thread control nicely. Passing thread control..."); var success = await FacebookThreadControlHelper.PassThreadControlAsync( _configuration["FacebookPageToken"], requestedOwnerAppId, facebookPayload.Sender.Id, "allowing thread control"); if (!success) { // Account for situations when the primary receiver doesn't have thread control await turnContext.SendActivityAsync("Primary Bot: Thread control could not be passed."); } } else { await turnContext.SendActivityAsync($"Primary Bot: {requestedOwnerAppId} requested thread control but did not ask nicely." + " Thread control will not be passed." + " Send any message to continue."); } } private static async Task ShowChoices(ITurnContext turnContext, CancellationToken cancellationToken) { // Create the message var message = ChoiceFactory.ForChannel(turnContext.Activity.ChannelId, _options, "Primary Bot: Please type a message or choose an option"); await turnContext.SendActivityAsync(message, cancellationToken); } } } ================================================ FILE: blog-samples/CSharp/FacebookHandover/Primary/Controllers/BotController.cs ================================================ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. // // Generated with Bot Builder V4 SDK Template for Visual Studio EchoBot v4.3.0 using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Microsoft.Bot.Builder; using Microsoft.Bot.Builder.Integration.AspNet.Core; namespace Primary.Controllers { // This ASP Controller is created to handle a request. Dependency Injection will provide the Adapter and IBot // implementation at runtime. Multiple different IBot implementations running at different endpoints can be // achieved by specifying a more specific type for the bot constructor argument. [Route("api/messages")] [ApiController] public class BotController : ControllerBase { private readonly IBotFrameworkHttpAdapter Adapter; private readonly IBot Bot; public BotController(IBotFrameworkHttpAdapter adapter, IBot bot) { Adapter = adapter; Bot = bot; } [HttpPost] public async Task PostAsync() { // Delegate the processing of the HTTP POST to the adapter. // The adapter will invoke the bot. await Adapter.ProcessAsync(Request, Response, Bot); } } } ================================================ FILE: blog-samples/CSharp/FacebookHandover/Primary/DeploymentTemplates/template-with-new-rg.json ================================================ { "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", "contentVersion": "1.0.0.0", "parameters": { "groupLocation": { "type": "string", "metadata": { "description": "Specifies the location of the Resource Group." } }, "groupName": { "type": "string", "metadata": { "description": "Specifies the name of the Resource Group." } }, "appId": { "type": "string", "metadata": { "description": "Active Directory App ID, set as MicrosoftAppId in the Web App's Application Settings." } }, "appSecret": { "type": "string", "metadata": { "description": "Active Directory App Password, set as MicrosoftAppPassword in the Web App's Application Settings." } }, "botId": { "type": "string", "metadata": { "description": "The globally unique and immutable bot ID. Also used to configure the displayName of the bot, which is mutable." } }, "botSku": { "type": "string", "metadata": { "description": "The pricing tier of the Bot Service Registration. Acceptable values are F0 and S1." } }, "newAppServicePlanName": { "type": "string", "metadata": { "description": "The name of the App Service Plan." } }, "newAppServicePlanSku": { "type": "object", "defaultValue": { "name": "S1", "tier": "Standard", "size": "S1", "family": "S", "capacity": 1 }, "metadata": { "description": "The SKU of the App Service Plan. Defaults to Standard values." } }, "newAppServicePlanLocation": { "type": "string", "metadata": { "description": "The location of the App Service Plan. Defaults to \"westus\"." } }, "newWebAppName": { "type": "string", "defaultValue": "", "metadata": { "description": "The globally unique name of the Web App. Defaults to the value passed in for \"botId\"." } } }, "variables": { "appServicePlanName": "[parameters('newAppServicePlanName')]", "resourcesLocation": "[parameters('newAppServicePlanLocation')]", "webAppName": "[if(empty(parameters('newWebAppName')), parameters('botId'), parameters('newWebAppName'))]", "siteHost": "[concat(variables('webAppName'), '.azurewebsites.net')]", "botEndpoint": "[concat('https://', variables('siteHost'), '/api/messages')]" }, "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 App Service Plan", "type": "Microsoft.Web/serverfarms", "name": "[variables('appServicePlanName')]", "apiVersion": "2018-02-01", "location": "[variables('resourcesLocation')]", "sku": "[parameters('newAppServicePlanSku')]", "properties": { "name": "[variables('appServicePlanName')]" } }, { "comments": "Create a Web App using the new App Service Plan", "type": "Microsoft.Web/sites", "apiVersion": "2015-08-01", "location": "[variables('resourcesLocation')]", "kind": "app", "dependsOn": [ "[resourceId('Microsoft.Web/serverfarms/', variables('appServicePlanName'))]" ], "name": "[variables('webAppName')]", "properties": { "name": "[variables('webAppName')]", "serverFarmId": "[variables('appServicePlanName')]", "siteConfig": { "appSettings": [ { "name": "WEBSITE_NODE_DEFAULT_VERSION", "value": "10.14.1" }, { "name": "MicrosoftAppId", "value": "[parameters('appId')]" }, { "name": "MicrosoftAppPassword", "value": "[parameters('appSecret')]" } ], "cors": { "allowedOrigins": [ "https://botservice.hosting.portal.azure.net", "https://hosting.onecloud.azure-test.net/" ] } } } }, { "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'))]" ] } ], "outputs": {} } } } ] } ================================================ FILE: blog-samples/CSharp/FacebookHandover/Primary/DeploymentTemplates/template-with-preexisting-rg.json ================================================ { "$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": "F0", "type": "string", "metadata": { "description": "The pricing tier of the Bot Service Registration. Acceptable values are F0 and S1." } }, "newAppServicePlanName": { "type": "string", "defaultValue": "", "metadata": { "description": "The name of the new App Service Plan." } }, "newAppServicePlanSku": { "type": "object", "defaultValue": { "name": "S1", "tier": "Standard", "size": "S1", "family": "S", "capacity": 1 }, "metadata": { "description": "The SKU of the App Service Plan. Defaults to Standard values." } }, "appServicePlanLocation": { "type": "string", "metadata": { "description": "The location of the App Service Plan." } }, "existingAppServicePlan": { "type": "string", "defaultValue": "", "metadata": { "description": "Name of the existing App Service Plan used to create the Web App for the bot." } }, "newWebAppName": { "type": "string", "defaultValue": "", "metadata": { "description": "The globally unique name of the Web App. Defaults to the value passed in for \"botId\"." } } }, "variables": { "defaultAppServicePlanName": "[if(empty(parameters('existingAppServicePlan')), 'createNewAppServicePlan', parameters('existingAppServicePlan'))]", "useExistingAppServicePlan": "[not(equals(variables('defaultAppServicePlanName'), 'createNewAppServicePlan'))]", "servicePlanName": "[if(variables('useExistingAppServicePlan'), parameters('existingAppServicePlan'), parameters('newAppServicePlanName'))]", "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 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')]", "properties": { "name": "[variables('servicePlanName')]" } }, { "comments": "Create a Web App using an App Service Plan", "type": "Microsoft.Web/sites", "apiVersion": "2015-08-01", "location": "[variables('resourcesLocation')]", "kind": "app", "dependsOn": [ "[resourceId('Microsoft.Web/serverfarms/', variables('servicePlanName'))]" ], "name": "[variables('webAppName')]", "properties": { "name": "[variables('webAppName')]", "serverFarmId": "[variables('servicePlanName')]", "siteConfig": { "appSettings": [ { "name": "WEBSITE_NODE_DEFAULT_VERSION", "value": "10.14.1" }, { "name": "MicrosoftAppId", "value": "[parameters('appId')]" }, { "name": "MicrosoftAppPassword", "value": "[parameters('appSecret')]" } ], "cors": { "allowedOrigins": [ "https://botservice.hosting.portal.azure.net", "https://hosting.onecloud.azure-test.net/" ] } } } }, { "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'))]" ] } ] } ================================================ FILE: blog-samples/CSharp/FacebookHandover/Primary/Primary.csproj ================================================  netcoreapp2.1 Always ================================================ FILE: blog-samples/CSharp/FacebookHandover/Primary/Program.cs ================================================ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. // // Generated with Bot Builder V4 SDK Template for Visual Studio EchoBot v4.3.0 using Microsoft.AspNetCore; using Microsoft.AspNetCore.Hosting; namespace Primary { public class Program { public static void Main(string[] args) { CreateWebHostBuilder(args).Build().Run(); } public static IWebHostBuilder CreateWebHostBuilder(string[] args) => WebHost.CreateDefaultBuilder(args) .UseStartup(); } } ================================================ FILE: blog-samples/CSharp/FacebookHandover/Primary/Properties/launchSettings.json ================================================ { "iisSettings": { "windowsAuthentication": false, "anonymousAuthentication": true, "iisExpress": { "applicationUrl": "http://localhost:3978/", "sslPort": 0 } }, "profiles": { "IIS Express": { "commandName": "IISExpress", "launchBrowser": true, "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } }, "Primary": { "commandName": "Project", "launchBrowser": true, "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" }, "applicationUrl": "http://localhost:3978/" } } } ================================================ FILE: blog-samples/CSharp/FacebookHandover/Primary/Startup.cs ================================================ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. // // Generated with Bot Builder V4 SDK Template for Visual Studio EchoBot v4.3.0 using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc; using Microsoft.Bot.Builder; using Microsoft.Bot.Builder.BotFramework; using Microsoft.Bot.Builder.Integration.AspNet.Core; using Microsoft.Bot.Connector.Authentication; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Primary.Bots; namespace Primary { public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; } public IConfiguration Configuration { get; } // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); // Create the credential provider to be used with the Bot Framework Adapter. services.AddSingleton(); // Create the Bot Framework Adapter. services.AddSingleton(); // Create the bot as a transient. In this case the ASP Controller is expecting an IBot. services.AddTransient(); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IHostingEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseHsts(); } app.UseDefaultFiles(); app.UseStaticFiles(); //app.UseHttpsRedirection(); app.UseMvc(); } } } ================================================ FILE: blog-samples/CSharp/FacebookHandover/Primary/appsettings.Development.json ================================================ { "Logging": { "LogLevel": { "Default": "Debug", "System": "Information", "Microsoft": "Information" } } } ================================================ FILE: blog-samples/CSharp/FacebookHandover/Primary/appsettings.json ================================================ { "MicrosoftAppId": "", "MicrosoftAppPassword": "", "FacebookPageToken": "" } ================================================ FILE: blog-samples/CSharp/FacebookHandover/Primary/wwwroot/default.htm ================================================  Primary
Primary Bot
Your bot is ready!
You can test your bot in the Bot Framework Emulator
by connecting to http://localhost:3978/api/messages.
Visit Azure Bot Service to register your bot and add it to
various channels. The bot's endpoint URL typically looks like this:
https://your_bots_hostname/api/messages
================================================ FILE: blog-samples/CSharp/FacebookHandover/README.md ================================================ # Facebook Handovers This is the sample code for the handover protocol blog post. These two bots are meant to be running simultaneously, while each is connected to its own Facebook app. ================================================ FILE: blog-samples/CSharp/FacebookHandover/Secondary/Bots/SecondaryBot.cs ================================================ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. // // Generated with Bot Builder V4 SDK Template for Visual Studio EchoBot v4.3.0 using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using FacebookModel; using Microsoft.Bot.Builder; using Microsoft.Bot.Builder.Dialogs.Choices; using Microsoft.Bot.Schema; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using Newtonsoft.Json.Linq; namespace Secondary.Bots { public class SecondaryBot : ActivityHandler { /// /// This option passes thread control from the secondary receiver to a primary receiver. /// private const string OPTION_PASS_PRIMARY_BOT = "Pass to primary"; /// /// This option is ignored by this bot. /// The primary bot is meant to be listening for this phrase as a standby event /// and respond to it by taking thread control. /// private const string OPTION_TAKE_THREAD_CONTROL = "Have control taken"; /// /// This is not an option for this bot, /// but this bot is meant to recognize the phrase in a standby event while the primary bot has thread control /// and respond to it by requesting thread control from the primary bot. /// private const string OPTION_REQUEST_THREAD_CONTROL = "Receive request"; /// /// This is not an option for this bot, /// but this bot is meant to recognize the phrase in a standby event while the primary bot has thread control /// and respond to it by requesting thread control from the primary bot with "polite" metadata. /// private const string OPTION_REQUEST_THREAD_CONTROL_NICELY = "Receive nice request"; private static readonly List _options = new[] { OPTION_PASS_PRIMARY_BOT, OPTION_TAKE_THREAD_CONTROL, }.Select(option => new Choice(option)).ToList(); private readonly ILogger _logger; private readonly IConfiguration _configuration; public SecondaryBot(ILogger logger, IConfiguration configuration) { _logger = logger; _configuration = configuration; } protected override async Task OnMessageActivityAsync(ITurnContext turnContext, CancellationToken cancellationToken) { var text = turnContext.Activity.Text; switch (text) { case OPTION_PASS_PRIMARY_BOT: await turnContext.SendActivityAsync("Secondary Bot: Passing thread control to the primary receiver..."); // A null target app ID will automatically pass control to the primary receiver await FacebookThreadControlHelper.PassThreadControlAsync(_configuration["FacebookPageToken"], null, turnContext.Activity.From.Id, text); break; case OPTION_TAKE_THREAD_CONTROL: // Do nothing because the primary receiver should react to this instead break; default: await ShowChoices(turnContext, cancellationToken); break; } } protected override async Task OnConversationUpdateActivityAsync(ITurnContext turnContext, CancellationToken cancellationToken) { _logger.LogInformation("SecondaryBot - Processing a ConversationUpdate Activity."); var facebookPayload = (turnContext.Activity.ChannelData as JObject)?.ToObject(); if (facebookPayload != null) { if (facebookPayload.PassThreadControl != null) { await turnContext.SendActivityAsync($"Secondary Bot: Thread control is now passed to {facebookPayload.PassThreadControl.NewOwnerAppId} with the message \"{facebookPayload.PassThreadControl.Metadata}\""); await ShowChoices(turnContext, cancellationToken); } else if (facebookPayload.TakeThreadControl != null) { await turnContext.SendActivityAsync($"Secondary Bot: Thread control was taken by the primary receiver with the message \"{facebookPayload.TakeThreadControl.Metadata}\"." + $" The previous thread owner was {facebookPayload.TakeThreadControl.PreviousOwnerAppId}." + $" Send any message to continue."); } } await base.OnConversationUpdateActivityAsync(turnContext, cancellationToken); } protected override async Task OnEventActivityAsync(ITurnContext turnContext, CancellationToken cancellationToken) { _logger.LogInformation("SecondaryBot - Processing an Event Activity."); // Analyze Facebook payload from EventActivity.Value await ProcessStandbyPayload(turnContext, turnContext.Activity.Value, cancellationToken); } private async Task ProcessStandbyPayload(ITurnContext turnContext, object data, CancellationToken cancellationToken) { if (turnContext.Activity.Name?.Equals("standby", StringComparison.InvariantCultureIgnoreCase) == true) { var standbys = (data as JObject)?.ToObject(); if (standbys != null) { foreach (var standby in standbys.Standbys) { await OnFacebookStandby(turnContext, standby, cancellationToken); } } } } protected virtual async Task OnFacebookStandby(ITurnContext turnContext, FacebookStandby facebookStandby, CancellationToken cancellationToken) { _logger.LogInformation("SecondaryBot - Standby message received."); var text = facebookStandby?.Message?.Text; if (text?.Equals(OPTION_REQUEST_THREAD_CONTROL, StringComparison.InvariantCultureIgnoreCase) == true) { await FacebookThreadControlHelper.RequestThreadControlAsync(_configuration["FacebookPageToken"], facebookStandby.Sender.Id, "give me control"); } else if (text?.Equals(OPTION_REQUEST_THREAD_CONTROL_NICELY, StringComparison.InvariantCultureIgnoreCase) == true) { await FacebookThreadControlHelper.RequestThreadControlAsync(_configuration["FacebookPageToken"], facebookStandby.Sender.Id, "please"); } } private static async Task ShowChoices(ITurnContext turnContext, CancellationToken cancellationToken) { // Create the message var message = ChoiceFactory.ForChannel(turnContext.Activity.ChannelId, _options, "Secondary Bot: Please type a message or choose an option"); await turnContext.SendActivityAsync(message, cancellationToken); } } } ================================================ FILE: blog-samples/CSharp/FacebookHandover/Secondary/Controllers/BotController.cs ================================================ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. // // Generated with Bot Builder V4 SDK Template for Visual Studio EchoBot v4.3.0 using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Microsoft.Bot.Builder; using Microsoft.Bot.Builder.Integration.AspNet.Core; namespace Secondary.Controllers { // This ASP Controller is created to handle a request. Dependency Injection will provide the Adapter and IBot // implementation at runtime. Multiple different IBot implementations running at different endpoints can be // achieved by specifying a more specific type for the bot constructor argument. [Route("api/messages")] [ApiController] public class BotController : ControllerBase { private readonly IBotFrameworkHttpAdapter Adapter; private readonly IBot Bot; public BotController(IBotFrameworkHttpAdapter adapter, IBot bot) { Adapter = adapter; Bot = bot; } [HttpPost] public async Task PostAsync() { // Delegate the processing of the HTTP POST to the adapter. // The adapter will invoke the bot. await Adapter.ProcessAsync(Request, Response, Bot); } } } ================================================ FILE: blog-samples/CSharp/FacebookHandover/Secondary/DeploymentTemplates/template-with-new-rg.json ================================================ { "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", "contentVersion": "1.0.0.0", "parameters": { "groupLocation": { "type": "string", "metadata": { "description": "Specifies the location of the Resource Group." } }, "groupName": { "type": "string", "metadata": { "description": "Specifies the name of the Resource Group." } }, "appId": { "type": "string", "metadata": { "description": "Active Directory App ID, set as MicrosoftAppId in the Web App's Application Settings." } }, "appSecret": { "type": "string", "metadata": { "description": "Active Directory App Password, set as MicrosoftAppPassword in the Web App's Application Settings." } }, "botId": { "type": "string", "metadata": { "description": "The globally unique and immutable bot ID. Also used to configure the displayName of the bot, which is mutable." } }, "botSku": { "type": "string", "metadata": { "description": "The pricing tier of the Bot Service Registration. Acceptable values are F0 and S1." } }, "newAppServicePlanName": { "type": "string", "metadata": { "description": "The name of the App Service Plan." } }, "newAppServicePlanSku": { "type": "object", "defaultValue": { "name": "S1", "tier": "Standard", "size": "S1", "family": "S", "capacity": 1 }, "metadata": { "description": "The SKU of the App Service Plan. Defaults to Standard values." } }, "newAppServicePlanLocation": { "type": "string", "metadata": { "description": "The location of the App Service Plan. Defaults to \"westus\"." } }, "newWebAppName": { "type": "string", "defaultValue": "", "metadata": { "description": "The globally unique name of the Web App. Defaults to the value passed in for \"botId\"." } } }, "variables": { "appServicePlanName": "[parameters('newAppServicePlanName')]", "resourcesLocation": "[parameters('newAppServicePlanLocation')]", "webAppName": "[if(empty(parameters('newWebAppName')), parameters('botId'), parameters('newWebAppName'))]", "siteHost": "[concat(variables('webAppName'), '.azurewebsites.net')]", "botEndpoint": "[concat('https://', variables('siteHost'), '/api/messages')]" }, "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 App Service Plan", "type": "Microsoft.Web/serverfarms", "name": "[variables('appServicePlanName')]", "apiVersion": "2018-02-01", "location": "[variables('resourcesLocation')]", "sku": "[parameters('newAppServicePlanSku')]", "properties": { "name": "[variables('appServicePlanName')]" } }, { "comments": "Create a Web App using the new App Service Plan", "type": "Microsoft.Web/sites", "apiVersion": "2015-08-01", "location": "[variables('resourcesLocation')]", "kind": "app", "dependsOn": [ "[resourceId('Microsoft.Web/serverfarms/', variables('appServicePlanName'))]" ], "name": "[variables('webAppName')]", "properties": { "name": "[variables('webAppName')]", "serverFarmId": "[variables('appServicePlanName')]", "siteConfig": { "appSettings": [ { "name": "WEBSITE_NODE_DEFAULT_VERSION", "value": "10.14.1" }, { "name": "MicrosoftAppId", "value": "[parameters('appId')]" }, { "name": "MicrosoftAppPassword", "value": "[parameters('appSecret')]" } ], "cors": { "allowedOrigins": [ "https://botservice.hosting.portal.azure.net", "https://hosting.onecloud.azure-test.net/" ] } } } }, { "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'))]" ] } ], "outputs": {} } } } ] } ================================================ FILE: blog-samples/CSharp/FacebookHandover/Secondary/DeploymentTemplates/template-with-preexisting-rg.json ================================================ { "$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": "F0", "type": "string", "metadata": { "description": "The pricing tier of the Bot Service Registration. Acceptable values are F0 and S1." } }, "newAppServicePlanName": { "type": "string", "defaultValue": "", "metadata": { "description": "The name of the new App Service Plan." } }, "newAppServicePlanSku": { "type": "object", "defaultValue": { "name": "S1", "tier": "Standard", "size": "S1", "family": "S", "capacity": 1 }, "metadata": { "description": "The SKU of the App Service Plan. Defaults to Standard values." } }, "appServicePlanLocation": { "type": "string", "metadata": { "description": "The location of the App Service Plan." } }, "existingAppServicePlan": { "type": "string", "defaultValue": "", "metadata": { "description": "Name of the existing App Service Plan used to create the Web App for the bot." } }, "newWebAppName": { "type": "string", "defaultValue": "", "metadata": { "description": "The globally unique name of the Web App. Defaults to the value passed in for \"botId\"." } } }, "variables": { "defaultAppServicePlanName": "[if(empty(parameters('existingAppServicePlan')), 'createNewAppServicePlan', parameters('existingAppServicePlan'))]", "useExistingAppServicePlan": "[not(equals(variables('defaultAppServicePlanName'), 'createNewAppServicePlan'))]", "servicePlanName": "[if(variables('useExistingAppServicePlan'), parameters('existingAppServicePlan'), parameters('newAppServicePlanName'))]", "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 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')]", "properties": { "name": "[variables('servicePlanName')]" } }, { "comments": "Create a Web App using an App Service Plan", "type": "Microsoft.Web/sites", "apiVersion": "2015-08-01", "location": "[variables('resourcesLocation')]", "kind": "app", "dependsOn": [ "[resourceId('Microsoft.Web/serverfarms/', variables('servicePlanName'))]" ], "name": "[variables('webAppName')]", "properties": { "name": "[variables('webAppName')]", "serverFarmId": "[variables('servicePlanName')]", "siteConfig": { "appSettings": [ { "name": "WEBSITE_NODE_DEFAULT_VERSION", "value": "10.14.1" }, { "name": "MicrosoftAppId", "value": "[parameters('appId')]" }, { "name": "MicrosoftAppPassword", "value": "[parameters('appSecret')]" } ], "cors": { "allowedOrigins": [ "https://botservice.hosting.portal.azure.net", "https://hosting.onecloud.azure-test.net/" ] } } } }, { "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'))]" ] } ] } ================================================ FILE: blog-samples/CSharp/FacebookHandover/Secondary/Program.cs ================================================ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. // // Generated with Bot Builder V4 SDK Template for Visual Studio EchoBot v4.3.0 using Microsoft.AspNetCore; using Microsoft.AspNetCore.Hosting; namespace Secondary { public class Program { public static void Main(string[] args) { CreateWebHostBuilder(args).Build().Run(); } public static IWebHostBuilder CreateWebHostBuilder(string[] args) => WebHost.CreateDefaultBuilder(args) .UseStartup(); } } ================================================ FILE: blog-samples/CSharp/FacebookHandover/Secondary/Properties/launchSettings.json ================================================ { "iisSettings": { "windowsAuthentication": false, "anonymousAuthentication": true, "iisExpress": { "applicationUrl": "http://localhost:3979/", "sslPort": 0 } }, "profiles": { "IIS Express": { "commandName": "IISExpress", "launchBrowser": true, "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } }, "Secondary": { "commandName": "Project", "launchBrowser": true, "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" }, "applicationUrl": "http://localhost:3979/" } } } ================================================ FILE: blog-samples/CSharp/FacebookHandover/Secondary/Secondary.csproj ================================================  netcoreapp2.1 Always ================================================ FILE: blog-samples/CSharp/FacebookHandover/Secondary/Startup.cs ================================================ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. // // Generated with Bot Builder V4 SDK Template for Visual Studio EchoBot v4.3.0 using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc; using Microsoft.Bot.Builder; using Microsoft.Bot.Builder.BotFramework; using Microsoft.Bot.Builder.Integration.AspNet.Core; using Microsoft.Bot.Connector.Authentication; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Secondary.Bots; namespace Secondary { public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; } public IConfiguration Configuration { get; } // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); // Create the credential provider to be used with the Bot Framework Adapter. services.AddSingleton(); // Create the Bot Framework Adapter. services.AddSingleton(); // Create the bot as a transient. In this case the ASP Controller is expecting an IBot. services.AddTransient(); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IHostingEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseHsts(); } app.UseDefaultFiles(); app.UseStaticFiles(); //app.UseHttpsRedirection(); app.UseMvc(); } } } ================================================ FILE: blog-samples/CSharp/FacebookHandover/Secondary/appsettings.Development.json ================================================ { "Logging": { "LogLevel": { "Default": "Debug", "System": "Information", "Microsoft": "Information" } } } ================================================ FILE: blog-samples/CSharp/FacebookHandover/Secondary/appsettings.json ================================================ { "MicrosoftAppId": "", "MicrosoftAppPassword": "", "FacebookPageToken": "" } ================================================ FILE: blog-samples/CSharp/FacebookHandover/Secondary/wwwroot/default.htm ================================================  Secondary
Secondary Bot
Your bot is ready!
You can test your bot in the Bot Framework Emulator
by connecting to http://localhost:3979/api/messages.
Visit Azure Bot Service to register your bot and add it to
various channels. The bot's endpoint URL typically looks like this:
https://your_bots_hostname/api/messages
================================================ FILE: blog-samples/CSharp/Luis-Scorable-QnA/.gitignore ================================================ ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. ## ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore # User-specific files *.suo *.user *.userosscache *.sln.docstates # User-specific files (MonoDevelop/Xamarin Studio) *.userprefs # Build results [Dd]ebug/ [Dd]ebugPublic/ [Rr]elease/ [Rr]eleases/ x64/ x86/ bld/ [Bb]in/ [Oo]bj/ [Ll]og/ # Visual Studio 2015 cache/options directory .vs/ # Uncomment if you have tasks that create the project's static files in wwwroot #wwwroot/ # MSTest test Results [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* # NUNIT *.VisualState.xml TestResult.xml # Build Results of an ATL Project [Dd]ebugPS/ [Rr]eleasePS/ dlldata.c # .NET Core project.lock.json project.fragment.lock.json artifacts/ **/Properties/launchSettings.json *_i.c *_p.c *_i.h *.ilk *.meta *.obj *.pch *.pdb *.pgc *.pgd *.rsp *.sbr *.tlb *.tli *.tlh *.tmp *.tmp_proj *.log *.vspscc *.vssscc .builds *.pidb *.svclog *.scc # Chutzpah Test files _Chutzpah* # Visual C++ cache files ipch/ *.aps *.ncb *.opendb *.opensdf *.sdf *.cachefile *.VC.db *.VC.VC.opendb # Visual Studio profiler *.psess *.vsp *.vspx *.sap # TFS 2012 Local Workspace $tf/ # Guidance Automation Toolkit *.gpState # ReSharper is a .NET coding add-in _ReSharper*/ *.[Rr]e[Ss]harper *.DotSettings.user # JustCode is a .NET coding add-in .JustCode # TeamCity is a build add-in _TeamCity* # DotCover is a Code Coverage Tool *.dotCover # Visual Studio code coverage results *.coverage *.coveragexml # NCrunch _NCrunch_* .*crunch*.local.xml nCrunchTemp_* # MightyMoose *.mm.* AutoTest.Net/ # Web workbench (sass) .sass-cache/ # Installshield output folder [Ee]xpress/ # DocProject is a documentation generator add-in DocProject/buildhelp/ DocProject/Help/*.HxT DocProject/Help/*.HxC DocProject/Help/*.hhc DocProject/Help/*.hhk DocProject/Help/*.hhp DocProject/Help/Html2 DocProject/Help/html # Click-Once directory publish/ # Publish Web Output *.[Pp]ublish.xml *.azurePubxml # TODO: Comment the next line if you want to checkin your web deploy settings # but database connection strings (with potential passwords) will be unencrypted *.pubxml *.publishproj # Microsoft Azure Web App publish settings. Comment the next line if you want to # checkin your Azure Web App publish settings, but sensitive information contained # in these scripts will be unencrypted PublishScripts/ # NuGet Packages *.nupkg # The packages folder can be ignored because of Package Restore **/packages/* # except build/, which is used as an MSBuild target. !**/packages/build/ # Uncomment if necessary however generally it will be regenerated when needed #!**/packages/repositories.config # NuGet v3's project.json files produces more ignorable files *.nuget.props *.nuget.targets # Microsoft Azure Build Output csx/ *.build.csdef # Microsoft Azure Emulator ecf/ rcf/ # Windows Store app package directories and files AppPackages/ BundleArtifacts/ Package.StoreAssociation.xml _pkginfo.txt # Visual Studio cache files # files ending in .cache can be ignored *.[Cc]ache # but keep track of directories ending in .cache !*.[Cc]ache/ # Others ClientBin/ ~$* *~ *.dbmdl *.dbproj.schemaview *.jfm *.pfx *.publishsettings orleans.codegen.cs # Since there are multiple workflows, uncomment next line to ignore bower_components # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) #bower_components/ # RIA/Silverlight projects Generated_Code/ # Backup & report files from converting an old project file # to a newer Visual Studio version. Backup files are not needed, # because we have git ;-) _UpgradeReport_Files/ Backup*/ UpgradeLog*.XML UpgradeLog*.htm # SQL Server files *.mdf *.ldf *.ndf # Business Intelligence projects *.rdl.data *.bim.layout *.bim_*.settings # Microsoft Fakes FakesAssemblies/ # GhostDoc plugin setting file *.GhostDoc.xml # Node.js Tools for Visual Studio .ntvs_analysis.dat node_modules/ # Typescript v1 declaration files typings/ # Visual Studio 6 build log *.plg # Visual Studio 6 workspace options file *.opt # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) *.vbw # Visual Studio LightSwitch build output **/*.HTMLClient/GeneratedArtifacts **/*.DesktopClient/GeneratedArtifacts **/*.DesktopClient/ModelManifest.xml **/*.Server/GeneratedArtifacts **/*.Server/ModelManifest.xml _Pvt_Extensions # Paket dependency manager .paket/paket.exe paket-files/ # FAKE - F# Make .fake/ # JetBrains Rider .idea/ *.sln.iml # CodeRush .cr/ # Python Tools for Visual Studio (PTVS) __pycache__/ *.pyc # Cake - Uncomment if you are using it # tools/** # !tools/packages.config # Telerik's JustMock configuration file *.jmconfig # BizTalk build output *.btp.cs *.btm.cs *.odx.cs *.xsd.cs ================================================ FILE: blog-samples/CSharp/Luis-Scorable-QnA/Luis-Scorable-Qna/App_Start/WebApiConfig.cs ================================================ using Newtonsoft.Json; using Newtonsoft.Json.Serialization; using System; using System.Collections.Generic; using System.Linq; using System.Web.Http; namespace Scorable { public static class WebApiConfig { public static void Register(HttpConfiguration config) { // Json settings config.Formatters.JsonFormatter.SerializerSettings.NullValueHandling = NullValueHandling.Ignore; config.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); config.Formatters.JsonFormatter.SerializerSettings.Formatting = Formatting.Indented; JsonConvert.DefaultSettings = () => new JsonSerializerSettings() { ContractResolver = new CamelCasePropertyNamesContractResolver(), Formatting = Newtonsoft.Json.Formatting.Indented, NullValueHandling = NullValueHandling.Ignore, }; // Web API configuration and services // Web API routes config.MapHttpAttributeRoutes(); config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{id}", defaults: new { id = RouteParameter.Optional } ); } } } ================================================ FILE: blog-samples/CSharp/Luis-Scorable-QnA/Luis-Scorable-Qna/Controllers/MessagesController.cs ================================================ using System.Net; using System.Net.Http; using System.Threading.Tasks; using System.Web.Http; using Microsoft.Bot.Builder.Dialogs; using Microsoft.Bot.Connector; namespace Scorable { [BotAuthentication] public class MessagesController : ApiController { /// /// POST: api/Messages /// Receive a message from a user and reply to it /// public async Task Post([FromBody]Activity activity) { if (activity.Type == ActivityTypes.Message) { await Conversation.SendAsync(activity, () => new Dialogs.RootDialog()); } else { HandleSystemMessage(activity); } var response = Request.CreateResponse(HttpStatusCode.OK); return response; } private Activity HandleSystemMessage(Activity message) { if (message.Type == ActivityTypes.DeleteUserData) { // Implement user deletion here // If we handle user deletion, return a real message } else if (message.Type == ActivityTypes.ConversationUpdate) { // Handle conversation state changes, like members being added and removed // Use Activity.MembersAdded and Activity.MembersRemoved and Activity.Action for info // Not available in all channels } else if (message.Type == ActivityTypes.ContactRelationUpdate) { // Handle add/remove from contact lists // Activity.From + Activity.Action represent what happened } else if (message.Type == ActivityTypes.Typing) { // Handle knowing tha the user is typing } else if (message.Type == ActivityTypes.Ping) { } return null; } } } ================================================ FILE: blog-samples/CSharp/Luis-Scorable-QnA/Luis-Scorable-Qna/Dialogs/CommonResponsesDialog.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using System.Web; using Microsoft.Bot.Builder.Dialogs; using Microsoft.Bot.Connector; namespace Scorable.Dialogs { public class CommonResponsesDialog : IDialog { private readonly string _messageToSend; private Activity _activity; public CommonResponsesDialog(string message) { _messageToSend = message; } // overload constructor to handle activities (card attachments) public CommonResponsesDialog(Activity activity) { var heroCard = new HeroCard { Title = "Help", Text = "Need assisstance?", Buttons = new List { new CardAction(ActionTypes.OpenUrl, "Contact Us", value: "https://stackoverflow.com/questions/tagged/botframework"), new CardAction(ActionTypes.OpenUrl, "FAQ", value: "https://docs.microsoft.com/bot-framework") } }; var reply = activity.CreateReply(); reply.Attachments.Add(heroCard.ToAttachment()); _activity = reply; } public async Task StartAsync(IDialogContext context) { if (string.IsNullOrEmpty(_messageToSend)) { await context.PostAsync(_activity); } else { await context.PostAsync(_messageToSend); } context.Done(null); } } } ================================================ FILE: blog-samples/CSharp/Luis-Scorable-QnA/Luis-Scorable-Qna/Dialogs/CommonResponsesScorable.cs ================================================ using System.Threading; using System.Threading.Tasks; using Microsoft.Bot.Builder.Dialogs; using Microsoft.Bot.Builder.Dialogs.Internals; using Microsoft.Bot.Builder.Internals.Fibers; using Microsoft.Bot.Builder.Scorables.Internals; using Microsoft.Bot.Connector; namespace Scorable.Dialogs { public partial class CommonResponsesScorable : ScorableBase { private readonly IDialogTask task; public CommonResponsesScorable(IDialogTask task) { SetField.NotNull(out this.task, nameof(task), task); } protected override async Task PrepareAsync(IActivity activity, CancellationToken token) { var message = activity as IMessageActivity; if (message != null && !string.IsNullOrWhiteSpace(message.Text)) { var msg = message.Text.ToLowerInvariant(); if (msg == "hello" || msg == "thank you" || msg == "goodbye") { return message.Text; } else if (msg.Contains("help")) { return message.Text; } else { //call another dialog } } return null; } protected override bool HasScore(IActivity item, string state) { return state != null; } protected override double GetScore(IActivity item, string state) { return 1.0; } protected override async Task PostAsync(IActivity item, string state, CancellationToken token) { var message = item as IMessageActivity; IDialog interruption = null; if (message != null) { var incomingMessage = message.Text.ToLowerInvariant(); var messageToSend = string.Empty; if (incomingMessage.Contains("help")) { var commonResponsesDialog = new CommonResponsesDialog((Activity)message); interruption = commonResponsesDialog.Void(); } else { if (incomingMessage == "hello") messageToSend = "Hi! I am a bot"; if (incomingMessage == "thank you") messageToSend = "You are welcome!"; if (incomingMessage == "goodbye") messageToSend = "See you later"; var commonResponsesDialog = new CommonResponsesDialog(messageToSend); interruption = commonResponsesDialog.Void(); } this.task.Call(interruption, null); await this.task.PollAsync(token); } } protected override Task DoneAsync(IActivity item, string state, CancellationToken token) { return Task.CompletedTask; } } } ================================================ FILE: blog-samples/CSharp/Luis-Scorable-QnA/Luis-Scorable-Qna/Dialogs/JokeDialog.cs ================================================ using System; using System.Net; using System.Threading; using System.Threading.Tasks; using Microsoft.Bot.Builder.Dialogs; using Microsoft.Bot.Connector; namespace Scorable.Dialogs { [Serializable] public class JokeDialog : IDialog { public Task StartAsync(IDialogContext context) { // Confirmation that we're in the JokeDialog, forwarded from the LUIS dialog string response = "What time does the duck wake up? At the quack of dawn!"; context.PostAsync(response); return Task.CompletedTask; } private async Task MessageReceivedAsync(IDialogContext context, IAwaitable result) { var activity = await result as Activity; } } } ================================================ FILE: blog-samples/CSharp/Luis-Scorable-QnA/Luis-Scorable-Qna/Dialogs/LuisDialog.cs ================================================ using System; using System.Threading.Tasks; using Microsoft.Bot.Builder.Dialogs; using Microsoft.Bot.Builder.Luis; using Microsoft.Bot.Builder.Luis.Models; using Microsoft.Bot.Connector; using System.Threading; namespace Scorable.Dialogs { [Serializable] [LuisModel("YourLuisAppID", "YourLuisAppPassword")] public class LuisDialog : LuisDialog { [LuisIntent("")] [LuisIntent("None")] public async Task None(IDialogContext context, LuisResult result) { string message = $"Sorry, I did not understand '{result.Query}'. Type 'help' if you need assistance."; // alternatively, you could forward to QnA Dialog if no intent is found await context.PostAsync(message); context.Wait(this.MessageReceived); } [LuisIntent("greeting")] public async Task Greeting(IDialogContext context, LuisResult result) { string message = $"Hello there"; await context.PostAsync(message); context.Wait(this.MessageReceived); } private ResumeAfter after() { return null; } [LuisIntent("weather")] public async Task Middle(IDialogContext context, LuisResult result) { // confirm we hit weather intent string message = $"Weather forecast is..."; await context.PostAsync(message); context.Wait(this.MessageReceived); } [LuisIntent("joke")] public async Task Joke(IDialogContext context, LuisResult result) { // confirm we hit joke intent string message = $"Let's see...I know a good joke..."; await context.PostAsync(message); await context.Forward(new JokeDialog(), ResumeAfterJokeDialog, context.Activity, CancellationToken.None); } [LuisIntent("question")] public async Task QnA(IDialogContext context, LuisResult result) { // confirm we hit QnA string message = $"Routing to QnA... "; await context.PostAsync(message); var userQuestion = (context.Activity as Activity).Text; await context.Forward(new QnaDialog(), ResumeAfterQnA, context.Activity, CancellationToken.None); } private async Task ResumeAfterQnA(IDialogContext context, IAwaitable result) { context.Done(null); } private async Task ResumeAfterJokeDialog(IDialogContext context, IAwaitable result) { context.Done(null); } } } ================================================ FILE: blog-samples/CSharp/Luis-Scorable-QnA/Luis-Scorable-Qna/Dialogs/QnaDialog.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.Bot.Connector; using Microsoft.Bot.Builder.Dialogs; using Microsoft.Bot.Builder.CognitiveServices.QnAMaker; using System.Configuration; using System.Net; namespace Scorable.Dialogs { public class QnaDialog : QnAMakerDialog { public QnaDialog(): base( new QnAMakerService(new QnAMakerAttribute(ConfigurationManager.AppSettings["QnaSubscriptionKey"], ConfigurationManager.AppSettings["QnaKnowledgebaseId"], "Hmm, I wasn't able to find an article about that. Can you try asking in a different way?", 0.5))) { } } } ================================================ FILE: blog-samples/CSharp/Luis-Scorable-QnA/Luis-Scorable-Qna/Dialogs/RootDialog.cs ================================================ using System; using System.Threading; using System.Threading.Tasks; using Microsoft.Bot.Builder.Dialogs; using Microsoft.Bot.Connector; namespace Scorable.Dialogs { [Serializable] public class RootDialog : IDialog { public Task StartAsync(IDialogContext context) { context.Wait(MessageReceivedAsync); return Task.CompletedTask; } private async Task MessageReceivedAsync(IDialogContext context, IAwaitable result) { var activity = await result as Activity; await context.Forward(new LuisDialog(), ResumeAftelLuisDialog, activity, CancellationToken.None); } private async Task ResumeAftelLuisDialog(IDialogContext context, IAwaitable result) { context.Wait(MessageReceivedAsync); } } } ================================================ FILE: blog-samples/CSharp/Luis-Scorable-QnA/Luis-Scorable-Qna/Global.asax ================================================ <%@ Application Codebehind="Global.asax.cs" Inherits="Scorable.WebApiApplication" Language="C#" %> ================================================ FILE: blog-samples/CSharp/Luis-Scorable-QnA/Luis-Scorable-Qna/Global.asax.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using System.Web; using System.Web.Http; using System.Web.Routing; using Autofac; using Microsoft.Bot.Builder.Dialogs; using Microsoft.Bot.Builder.Dialogs.Internals; using Microsoft.Bot.Builder.Internals.Fibers; using Microsoft.Bot.Builder.Scorables; using Microsoft.Bot.Connector; using Scorable.Dialogs; namespace Scorable { public class WebApiApplication : System.Web.HttpApplication { protected void Application_Start() { this.RegisterBotModules(); GlobalConfiguration.Configure(WebApiConfig.Register); } private void RegisterBotModules() { var builder = new ContainerBuilder(); builder.RegisterModule(new ReflectionSurrogateModule()); builder.RegisterModule(); builder.Update(Conversation.Container); } } public class GlobalMessageHandlersBotModule : Module { protected override void Load(ContainerBuilder builder) { base.Load(builder); builder .Register(c => new CommonResponsesScorable(c.Resolve())) .As>() .InstancePerLifetimeScope(); } } } ================================================ FILE: blog-samples/CSharp/Luis-Scorable-QnA/Luis-Scorable-Qna/Luis-Scorable-Qna-Demo.csproj ================================================  Debug AnyCPU 2.0 {9CB693F7-95B3-4055-955D-BC75BC2D706B} {349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} Library Properties Scorable Bot Application v4.6 true true full false bin\ DEBUG;TRACE prompt 4 pdbonly true bin\ TRACE prompt 4 $(SolutionDir)\packages\Autofac.3.5.2\lib\net40\Autofac.dll $(SolutionDir)\packages\Chronic.Signed.0.3.2\lib\net40\Chronic.dll ..\packages\Microsoft.Bot.Builder.3.11.0\lib\net46\Microsoft.Bot.Builder.dll ..\packages\Microsoft.Bot.Builder.3.11.0\lib\net46\Microsoft.Bot.Builder.Autofac.dll ..\packages\Microsoft.Bot.Builder.CognitiveServices.1.1.1\lib\net46\Microsoft.Bot.Builder.CognitiveServices.QnAMaker.dll ..\packages\Microsoft.Bot.Connector.3.11.1\lib\net45\Microsoft.Bot.Connector.dll $(SolutionDir)\packages\Microsoft.IdentityModel.Protocol.Extensions.1.0.4.403061554\lib\net45\Microsoft.IdentityModel.Protocol.Extensions.dll $(SolutionDir)\packages\Microsoft.Rest.ClientRuntime.2.3.2\lib\net45\Microsoft.Rest.ClientRuntime.dll $(SolutionDir)\packages\Microsoft.WindowsAzure.ConfigurationManager.3.1.0\lib\net40\Microsoft.WindowsAzure.Configuration.dll $(SolutionDir)\packages\Newtonsoft.Json.8.0.3\lib\net45\Newtonsoft.Json.dll $(SolutionDir)\packages\System.IdentityModel.Tokens.Jwt.4.0.4.403061554\lib\net45\System.IdentityModel.Tokens.Jwt.dll $(SolutionDir)\packages\Microsoft.AspNet.WebApi.Client.5.2.3\lib\net45\System.Net.Http.Formatting.dll $(SolutionDir)\packages\Microsoft.AspNet.WebApi.Core.5.2.3\lib\net45\System.Web.Http.dll $(SolutionDir)\packages\Microsoft.AspNet.WebApi.WebHost.5.2.3\lib\net45\System.Web.Http.WebHost.dll Designer Global.asax Designer Web.config Web.config 10.0 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) true True True 3979 / http://localhost:3980/ False False False ================================================ FILE: blog-samples/CSharp/Luis-Scorable-QnA/Luis-Scorable-Qna/Properties/AssemblyInfo.cs ================================================ using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. [assembly: AssemblyTitle("Scorable")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("Scorable")] [assembly: AssemblyCopyright("Copyright © 2016")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] // Setting ComVisible to false makes the types in this assembly not visible // to COM components. If you need to access a type in this assembly from // COM, set the ComVisible attribute to true on that type. [assembly: ComVisible(false)] // The following GUID is for the ID of the typelib if this project is exposed to COM [assembly: Guid("350db888-cab7-4454-99db-c928c7832920")] // Version information for an assembly consists of the following four values: // // Major Version // Minor Version // Build Number // Revision // // You can specify all the values or you can default the Revision and Build Numbers // by using the '*' as shown below: [assembly: AssemblyVersion("1.0.0.0")] [assembly: AssemblyFileVersion("1.0.0.0")] ================================================ FILE: blog-samples/CSharp/Luis-Scorable-QnA/Luis-Scorable-Qna/Web.Debug.config ================================================ ================================================ FILE: blog-samples/CSharp/Luis-Scorable-QnA/Luis-Scorable-Qna/Web.Release.config ================================================ ================================================ FILE: blog-samples/CSharp/Luis-Scorable-QnA/Luis-Scorable-Qna/Web.config ================================================  ================================================ FILE: blog-samples/CSharp/Luis-Scorable-QnA/Luis-Scorable-Qna/default.htm ================================================ 

Luis Scorable Qna Demo Bot

Describe your bot here and your terms of use etc.

Visit Bot Framework to register your bot. When you register it, remember to set your bot's endpoint to

https://your_bots_hostname/api/messages

================================================ FILE: blog-samples/CSharp/Luis-Scorable-QnA/Luis-Scorable-Qna/packages.config ================================================  ================================================ FILE: blog-samples/CSharp/Luis-Scorable-QnA/Luis-Scorable-Qna.sln ================================================  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 VisualStudioVersion = 15.0.27004.2002 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Luis-Scorable-Qna-Demo", "Luis-Scorable-Qna\Luis-Scorable-Qna-Demo.csproj", "{9CB693F7-95B3-4055-955D-BC75BC2D706B}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {9CB693F7-95B3-4055-955D-BC75BC2D706B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {9CB693F7-95B3-4055-955D-BC75BC2D706B}.Debug|Any CPU.Build.0 = Debug|Any CPU {9CB693F7-95B3-4055-955D-BC75BC2D706B}.Release|Any CPU.ActiveCfg = Release|Any CPU {9CB693F7-95B3-4055-955D-BC75BC2D706B}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {76AE3092-2BB0-4A8C-9E81-A15B8E9D662E} EndGlobalSection EndGlobal ================================================ FILE: blog-samples/CSharp/MockChannel/App_Start/WebApiConfig.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Web.Http; namespace MockChannel { public static class WebApiConfig { public static void Register(HttpConfiguration config) { // Web API configuration and services // Web API routes config.MapHttpAttributeRoutes(); config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{id}", defaults: new { id = RouteParameter.Optional } ); } } } ================================================ FILE: blog-samples/CSharp/MockChannel/Controllers/MockChannelController.cs ================================================ using Microsoft.Bot.Connector; using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Http; using System.Threading.Tasks; using System.Web.Http; using System.Web.Http.Description; namespace MockChannel.Controllers { [RoutePrefix("v3/conversations")] public class MockChannelController : ApiController { /// /// CreateConversation /// /// /// Markdown=Content\Methods\CreateConversation.md /// /// Parameters to create the conversation from [HttpPost] [Route("")] public HttpResponseMessage CreateConversation([FromBody]ConversationParameters parameters) { Uri uri = new Uri(Request.RequestUri, "/"); var id = Guid.NewGuid().ToString("n"); return Request.CreateResponse(HttpStatusCode.Created, new ConversationResourceResponse(id: id, serviceUrl: uri.ToString())); } /// /// SendToConversation /// /// /// Markdown=Content\Methods\SendToConversation.md /// /// Conversation ID /// Activity to send [HttpPost] [Route("{conversationId}/activities")] public HttpResponseMessage SendToConversation(string conversationId, [FromBody]Activity activity) { var id = Guid.NewGuid().ToString("n"); return Request.CreateResponse(HttpStatusCode.OK, new ResourceResponse(id: id)); } /// /// ReplyToActivity /// /// /// Markdown=Content\Methods\ReplyToActivity.md /// /// Activity to send /// Conversation ID /// activityId the reply is to (OPTIONAL) [HttpPost] [Route("{conversationId}/activities/{activityId}")] public HttpResponseMessage ReplyToActivity(string conversationId, string activityId, [FromBody]Activity activity) { var id = Guid.NewGuid().ToString("n"); return Request.CreateResponse(HttpStatusCode.OK, new ResourceResponse(id: id)); } /// /// UpdateActivity /// /// /// Markdown=Content\Methods\UpdateActivity.md /// /// Conversation ID /// activityId to update /// replacement Activity [HttpPut] [Route("{conversationId}/activities/{activityId}")] public HttpResponseMessage UpdateActivity(string conversationId, string activityId, [FromBody]Activity activity) { return Request.CreateResponse(HttpStatusCode.OK, new ResourceResponse(id: activity.Id)); } /// /// DeleteActivity /// /// /// Markdown=Content\Methods\DeleteActivity.md /// /// Conversation ID /// activityId to delete [HttpDelete] [Route("{conversationId}/activities/{activityId}")] public HttpResponseMessage DeleteActivity(string conversationId, string activityId) { return Request.CreateResponse(HttpStatusCode.OK); } /// /// GetConversationMembers /// /// /// Markdown=Content\Methods\GetConversationMembers.md /// /// Conversation ID [HttpGet] [Route("{conversationId}/members")] public HttpResponseMessage GetConversationMembers(string conversationId) { return Request.CreateResponse(HttpStatusCode.OK, new ChannelAccount[0]); } /// /// GetActivityMembers /// /// /// Markdown=Content\Methods\GetActivityMembers.md /// /// Conversation ID /// Activity ID [HttpGet] [Route("{conversationId}/activities/{activityId}/members")] public HttpResponseMessage GetActivityMembers(string conversationId, string activityId) { return Request.CreateResponse(HttpStatusCode.OK, new ChannelAccount[0]); } /// /// UploadAttachment /// /// /// Markdown=Content\Methods\UploadAttachment.md /// /// Conversation ID /// Attachment data [HttpPost] [Route("{conversationId}/attachments")] public HttpResponseMessage UploadAttachment(string conversationId, [FromBody]AttachmentData attachmentUpload) { var id = Guid.NewGuid().ToString("n"); return Request.CreateResponse(HttpStatusCode.OK, new ResourceResponse(id: id)); } } } ================================================ FILE: blog-samples/CSharp/MockChannel/Global.asax ================================================ <%@ Application Codebehind="Global.asax.cs" Inherits="MockChannel.WebApiApplication" Language="C#" %> ================================================ FILE: blog-samples/CSharp/MockChannel/Global.asax.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Http; using System.Web.Routing; namespace MockChannel { public class WebApiApplication : System.Web.HttpApplication { protected void Application_Start() { GlobalConfiguration.Configure(WebApiConfig.Register); } } } ================================================ FILE: blog-samples/CSharp/MockChannel/MockChannel.csproj ================================================  Debug AnyCPU 2.0 {5BC119EB-69F3-45BE-A394-5AAD86D3D355} {349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} Library Properties MockChannel MockChannel v4.6.1 true true full false bin\ DEBUG;TRACE prompt 4 true pdbonly true bin\ TRACE prompt 4 ..\packages\Microsoft.Bot.Connector.3.14.0\lib\net46\Microsoft.Bot.Connector.dll ..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.1.0.7\lib\net45\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.dll ..\packages\Microsoft.IdentityModel.Logging.1.1.4\lib\net451\Microsoft.IdentityModel.Logging.dll ..\packages\Microsoft.IdentityModel.Protocols.2.1.4\lib\net451\Microsoft.IdentityModel.Protocols.dll ..\packages\Microsoft.IdentityModel.Protocols.OpenIdConnect.2.1.4\lib\net451\Microsoft.IdentityModel.Protocols.OpenIdConnect.dll ..\packages\Microsoft.IdentityModel.Tokens.5.1.4\lib\net451\Microsoft.IdentityModel.Tokens.dll ..\packages\Microsoft.Rest.ClientRuntime.2.3.2\lib\net45\Microsoft.Rest.ClientRuntime.dll ..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll ..\packages\System.IdentityModel.Tokens.Jwt.5.1.4\lib\net451\System.IdentityModel.Tokens.Jwt.dll ..\packages\Microsoft.AspNet.WebApi.Client.5.2.3\lib\net45\System.Net.Http.Formatting.dll ..\packages\Microsoft.AspNet.WebApi.Core.5.2.3\lib\net45\System.Web.Http.dll ..\packages\Microsoft.AspNet.WebApi.WebHost.5.2.3\lib\net45\System.Web.Http.WebHost.dll Global.asax Web.config Web.config 10.0 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) True True 51753 / http://localhost:51753/ False False False This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. ================================================ FILE: blog-samples/CSharp/MockChannel/MockChannel.sln ================================================  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 VisualStudioVersion = 15.0.27130.2036 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MockChannel", "MockChannel.csproj", "{5BC119EB-69F3-45BE-A394-5AAD86D3D355}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {5BC119EB-69F3-45BE-A394-5AAD86D3D355}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {5BC119EB-69F3-45BE-A394-5AAD86D3D355}.Debug|Any CPU.Build.0 = Debug|Any CPU {5BC119EB-69F3-45BE-A394-5AAD86D3D355}.Release|Any CPU.ActiveCfg = Release|Any CPU {5BC119EB-69F3-45BE-A394-5AAD86D3D355}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {AD9C2B50-4B24-45F5-A73F-A1DF4FCF1AC8} EndGlobalSection EndGlobal ================================================ FILE: blog-samples/CSharp/MockChannel/Properties/AssemblyInfo.cs ================================================ using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. [assembly: AssemblyTitle("MockChannel")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("MockChannel")] [assembly: AssemblyCopyright("Copyright © 2018")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] // Setting ComVisible to false makes the types in this assembly not visible // to COM components. If you need to access a type in this assembly from // COM, set the ComVisible attribute to true on that type. [assembly: ComVisible(false)] // The following GUID is for the ID of the typelib if this project is exposed to COM [assembly: Guid("5bc119eb-69f3-45be-a394-5aad86d3d355")] // Version information for an assembly consists of the following four values: // // Major Version // Minor Version // Build Number // Revision // // You can specify all the values or you can default the Revision and Build Numbers // by using the '*' as shown below: [assembly: AssemblyVersion("1.0.0.0")] [assembly: AssemblyFileVersion("1.0.0.0")] ================================================ FILE: blog-samples/CSharp/MockChannel/README.md ================================================ # MockChannel When creating load tests as described in [Load testing a Bot](https://blog.botframework.com/2017/06/19/Load-Testing-A-Bot/) you need a service to pass as the activity.ServiceUrl. This is a sample implementation of that callback service. To use: * build and deploy (say http://yourmockservice.azurewebsites.net) * When posting an activity to your bot from your webTest, set the activity.ServiceUrl = "http://yourmockservice.azurewebsites.net" When you run your load test generating requests to your bot, when the bot posts back it will post back to the ServiceUrl in the activity which is this mock service. ================================================ FILE: blog-samples/CSharp/MockChannel/Web.Debug.config ================================================ ================================================ FILE: blog-samples/CSharp/MockChannel/Web.Release.config ================================================ ================================================ FILE: blog-samples/CSharp/MockChannel/Web.config ================================================  ================================================ FILE: blog-samples/CSharp/MockChannel/packages.config ================================================  ================================================ FILE: blog-samples/CSharp/Qna-Rich-Cards/Qna-Rich-Cards/AnswerFormats/JsonQnaAnswer.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Web; namespace Qna_Rich_Cards.AnswerFormats { public class JsonQnaAnswer { public string title { get; set; } public string desc { get; set; } public string url { get; set; } } } ================================================ FILE: blog-samples/CSharp/Qna-Rich-Cards/Qna-Rich-Cards/App_Start/WebApiConfig.cs ================================================ using System.Web.Http; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; namespace Qna_Rich_Cards { public static class WebApiConfig { public static void Register(HttpConfiguration config) { // Json settings config.Formatters.JsonFormatter.SerializerSettings.NullValueHandling = NullValueHandling.Ignore; config.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); config.Formatters.JsonFormatter.SerializerSettings.Formatting = Formatting.Indented; JsonConvert.DefaultSettings = () => new JsonSerializerSettings() { ContractResolver = new CamelCasePropertyNamesContractResolver(), Formatting = Newtonsoft.Json.Formatting.Indented, NullValueHandling = NullValueHandling.Ignore, }; // Web API configuration and services // Web API routes config.MapHttpAttributeRoutes(); config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{id}", defaults: new { id = RouteParameter.Optional } ); } } } ================================================ FILE: blog-samples/CSharp/Qna-Rich-Cards/Qna-Rich-Cards/Controllers/MessagesController.cs ================================================ using System.Net; using System.Net.Http; using System.Threading.Tasks; using System.Web.Http; using Microsoft.Bot.Builder.Dialogs; using Microsoft.Bot.Connector; namespace Qna_Rich_Cards { [BotAuthentication] public class MessagesController : ApiController { /// /// POST: api/Messages /// Receive a message from a user and reply to it /// public async Task Post([FromBody]Activity activity) { if (activity.Type == ActivityTypes.Message) { await Conversation.SendAsync(activity, () => new Dialogs.QnaDialog()); // await Conversation.SendAsync(activity, () => new Dialogs.RootDialog()); } else { HandleSystemMessage(activity); } var response = Request.CreateResponse(HttpStatusCode.OK); return response; } private Activity HandleSystemMessage(Activity message) { if (message.Type == ActivityTypes.DeleteUserData) { // Implement user deletion here // If we handle user deletion, return a real message } else if (message.Type == ActivityTypes.ConversationUpdate) { // Handle conversation state changes, like members being added and removed // Use Activity.MembersAdded and Activity.MembersRemoved and Activity.Action for info // Not available in all channels } else if (message.Type == ActivityTypes.ContactRelationUpdate) { // Handle add/remove from contact lists // Activity.From + Activity.Action represent what happened } else if (message.Type == ActivityTypes.Typing) { // Handle knowing tha the user is typing } else if (message.Type == ActivityTypes.Ping) { } return null; } } } ================================================ FILE: blog-samples/CSharp/Qna-Rich-Cards/Qna-Rich-Cards/Dialogs/QnaDialog.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using System.Configuration; using Microsoft.Bot.Connector; using Microsoft.Bot.Builder.Dialogs; using Microsoft.Bot.Builder.CognitiveServices.QnAMaker; using Qna_Rich_Cards.AnswerFormats; using Newtonsoft.Json.Linq; namespace Qna_Rich_Cards.Dialogs { [Serializable] public class QnaDialog : QnAMakerDialog { public QnaDialog() : base(new QnAMakerService(new QnAMakerAttribute(ConfigurationManager.AppSettings["QnaSubscriptionKey"], ConfigurationManager.AppSettings["QnaKnowledgebaseId"], "Sorry, I couldn't find an answer for that", 0.5))) { } protected override async Task RespondFromQnAMakerResultAsync(IDialogContext context, IMessageActivity message, QnAMakerResults result) { // answer is a string var answer = result.Answers.First().Answer; Activity reply = ((Activity)context.Activity).CreateReply(); string[] qnaAnswerData = answer.Split(';'); int dataSize = qnaAnswerData.Length; string title = qnaAnswerData[0]; string description = qnaAnswerData[1]; string url = qnaAnswerData[2]; string imageURL = qnaAnswerData[3]; HeroCard card = new HeroCard { Title = title, Subtitle = description, }; card.Buttons = new List { new CardAction(ActionTypes.OpenUrl, "Learn More", value: url) }; card.Images = new List { new CardImage( url = imageURL) }; reply.Attachments.Add(card.ToAttachment()); // TODO: Refactor sample // *********************************************************************************** // Example using JSON formatted answer from QnA, using model from JsonQnaAnswer.cs //******************************************************************************** // JsonQnaAnswer qnaAnswer = new JsonQnaAnswer(); // Activity reply = ((Activity)context.Activity).CreateReply(); // var response = JObject.Parse(answer); // qnaAnswer.title = response.Value("title"); // qnaAnswer.desc = response.Value("desc"); // qnaAnswer.url = response.Value("url"); // ThumbnailCard card = new ThumbnailCard() // { // Title = qnaAnswer.title, // Subtitle = qnaAnswer.desc, // Buttons = new List // { // new CardAction(ActionTypes.OpenUrl, "Click Here", value: qnaAnswer.url) // } // }; // reply.Attachments.Add(card.ToAttachment()); await context.PostAsync(reply); } } } ================================================ FILE: blog-samples/CSharp/Qna-Rich-Cards/Qna-Rich-Cards/Dialogs/RootDialog.cs ================================================ using System; using System.Threading.Tasks; using Microsoft.Bot.Builder.Dialogs; using Microsoft.Bot.Connector; namespace Qna_Rich_Cards.Dialogs { [Serializable] public class RootDialog : IDialog { public Task StartAsync(IDialogContext context) { context.Wait(MessageReceivedAsync); return Task.CompletedTask; } private async Task MessageReceivedAsync(IDialogContext context, IAwaitable result) { var activity = await result as Activity; // calculate something for us to return int length = (activity.Text ?? string.Empty).Length; // return our reply to the user await context.PostAsync($"You sent {activity.Text} which was {length} characters"); context.Wait(MessageReceivedAsync); } } } ================================================ FILE: blog-samples/CSharp/Qna-Rich-Cards/Qna-Rich-Cards/Global.asax ================================================ <%@ Application Codebehind="Global.asax.cs" Inherits="Qna_Rich_Cards.WebApiApplication" Language="C#" %> ================================================ FILE: blog-samples/CSharp/Qna-Rich-Cards/Qna-Rich-Cards/Global.asax.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Http; using System.Web.Routing; namespace Qna_Rich_Cards { public class WebApiApplication : System.Web.HttpApplication { protected void Application_Start() { GlobalConfiguration.Configure(WebApiConfig.Register); } } } ================================================ FILE: blog-samples/CSharp/Qna-Rich-Cards/Qna-Rich-Cards/Properties/AssemblyInfo.cs ================================================ using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. [assembly: AssemblyTitle("Qna_Rich_Cards")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("Qna_Rich_Cards")] [assembly: AssemblyCopyright("Copyright © 2016")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] // Setting ComVisible to false makes the types in this assembly not visible // to COM components. If you need to access a type in this assembly from // COM, set the ComVisible attribute to true on that type. [assembly: ComVisible(false)] // The following GUID is for the ID of the typelib if this project is exposed to COM [assembly: Guid("2fcba77f-8cc4-4ad2-b8e8-b41984905ac9")] // Version information for an assembly consists of the following four values: // // Major Version // Minor Version // Build Number // Revision // // You can specify all the values or you can default the Revision and Build Numbers // by using the '*' as shown below: [assembly: AssemblyVersion("1.0.0.0")] [assembly: AssemblyFileVersion("1.0.0.0")] ================================================ FILE: blog-samples/CSharp/Qna-Rich-Cards/Qna-Rich-Cards/Qna-Rich-Cards.csproj ================================================  Debug AnyCPU 2.0 {A8BA1066-5695-4D71-ABB4-65E5A5E0C3D4} {349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} Library Properties Qna_Rich_Cards Bot Application v4.6 true true full false bin\ DEBUG;TRACE prompt 4 pdbonly true bin\ TRACE prompt 4 $(SolutionDir)\packages\Autofac.3.5.2\lib\net40\Autofac.dll $(SolutionDir)\packages\Chronic.Signed.0.3.2\lib\net40\Chronic.dll $(SolutionDir)\packages\Microsoft.Bot.Builder.3.5.5\lib\net46\Microsoft.Bot.Builder.dll $(SolutionDir)\packages\Microsoft.Bot.Builder.3.5.5\lib\net46\Microsoft.Bot.Builder.Autofac.dll ..\packages\Microsoft.Bot.Builder.CognitiveServices.1.1.0\lib\net46\Microsoft.Bot.Builder.CognitiveServices.QnAMaker.dll $(SolutionDir)\packages\Microsoft.Bot.Builder.3.5.5\lib\net46\Microsoft.Bot.Connector.dll $(SolutionDir)\packages\Microsoft.IdentityModel.Protocol.Extensions.1.0.2.206221351\lib\net45\Microsoft.IdentityModel.Protocol.Extensions.dll $(SolutionDir)\packages\Microsoft.Rest.ClientRuntime.2.3.2\lib\net45\Microsoft.Rest.ClientRuntime.dll $(SolutionDir)\packages\Microsoft.WindowsAzure.ConfigurationManager.3.1.0\lib\net40\Microsoft.WindowsAzure.Configuration.dll $(SolutionDir)\packages\Newtonsoft.Json.8.0.3\lib\net45\Newtonsoft.Json.dll $(SolutionDir)\packages\System.IdentityModel.Tokens.Jwt.4.0.2.206221351\lib\net45\System.IdentityModel.Tokens.Jwt.dll $(SolutionDir)\packages\Microsoft.AspNet.WebApi.Client.5.2.3\lib\net45\System.Net.Http.Formatting.dll $(SolutionDir)\packages\Microsoft.AspNet.WebApi.Core.5.2.3\lib\net45\System.Web.Http.dll $(SolutionDir)\packages\Microsoft.AspNet.WebApi.WebHost.5.2.3\lib\net45\System.Web.Http.WebHost.dll Designer Global.asax Web.config Web.config 10.0 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) true True True 3979 / http://localhost:3979/ False False False ================================================ FILE: blog-samples/CSharp/Qna-Rich-Cards/Qna-Rich-Cards/Web.Debug.config ================================================ ================================================ FILE: blog-samples/CSharp/Qna-Rich-Cards/Qna-Rich-Cards/Web.Release.config ================================================ ================================================ FILE: blog-samples/CSharp/Qna-Rich-Cards/Qna-Rich-Cards/Web.config ================================================  ================================================ FILE: blog-samples/CSharp/Qna-Rich-Cards/Qna-Rich-Cards/default.htm ================================================ 

Qna_Rich_Cards

Describe your bot here and your terms of use etc.

Visit Bot Framework to register your bot. When you register it, remember to set your bot's endpoint to

https://your_bots_hostname/api/messages

================================================ FILE: blog-samples/CSharp/Qna-Rich-Cards/Qna-Rich-Cards/packages.config ================================================  ================================================ FILE: blog-samples/CSharp/Qna-Rich-Cards/Qna-Rich-Cards.sln ================================================  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 VisualStudioVersion = 15.0.26430.14 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Qna-Rich-Cards", "Qna-Rich-Cards\Qna-Rich-Cards.csproj", "{A8BA1066-5695-4D71-ABB4-65E5A5E0C3D4}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {A8BA1066-5695-4D71-ABB4-65E5A5E0C3D4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A8BA1066-5695-4D71-ABB4-65E5A5E0C3D4}.Debug|Any CPU.Build.0 = Debug|Any CPU {A8BA1066-5695-4D71-ABB4-65E5A5E0C3D4}.Release|Any CPU.ActiveCfg = Release|Any CPU {A8BA1066-5695-4D71-ABB4-65E5A5E0C3D4}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection EndGlobal ================================================ FILE: blog-samples/CSharp/Qna-Rich-Cards/README.md ================================================ # QnA Rich Cards Bot Sample This bot sample using the .NET SDK is to demonstrate two things: 1. How to connect a bot to a QnA service using the [Bot Builder Cognitive Services](https://www.nuget.org/packages/Microsoft.Bot.Builder.CognitiveServices/) NuGet package, open source on Github [here](https://github.com/Microsoft/BotBuilder-CognitiveServices). 2. How to implement overrides to the default QnAMakerDialog implementation such that a developer can 'intercept' the response activity from the QnA service and customize the reply to be posted back to a user. In this sample bot, we format the response from the QnA service into [rich card attachments](https://docs.microsoft.com/en-us/bot-framework/dotnet/bot-builder-dotnet-add-rich-card-attachments). ![Qna Rich Card response][pic1] ## QnA Maker overview [Click here for the QnA Maker portal](https://qnamaker.ai/) ![QnA Portal][pic2] One of the basic requirements in writing your own Bot service is to seed it with questions and answers. In many cases, the questions and answers already exist in content like FAQ URLs/documents, etc. Microsoft QnA Maker is a free, easy-to-use, REST API and web-based service that trains AI to respond to user's questions in a more natural, conversational way. Compatible across development platforms, hosting services, and channels, QnA Maker is the only question and answer service with a graphical user interface—meaning you don’t need to be a developer to train, manage, and use it for a wide range of solutions. With optimized machine learning logic and the ability to integrate industry-leading language processing with ease, QnA Maker distills masses of information into distinct, helpful answers. ## Prerequisites - [Visual Studio 2015 or 2017 Community](https://www.visualstudio.com/downloads/) - [Bot Application Template](http://aka.ms/bf-bc-vstemplate) - [BotBuilder-CognitiveServices](https://www.nuget.org/packages/Microsoft.Bot.Builder.CognitiveServices/) NuGet package - [Bot Framework Emulator](https://docs.microsoft.com/en-us/bot-framework/debug-bots-emulator) [pic1]: ../../images/qna-rich-cards.png [pic2]: ../../images/qna-portal.png ================================================ FILE: blog-samples/CSharp/ScorableBotSample/.gitattributes ================================================ ############################################################################### # Set default behavior to automatically normalize line endings. ############################################################################### * text=auto ############################################################################### # Set default behavior for command prompt diff. # # This is need for earlier builds of msysgit that does not have it on by # default for csharp files. # Note: This is only used by command line ############################################################################### #*.cs diff=csharp ############################################################################### # Set the merge driver for project and solution files # # Merging from the command prompt will add diff markers to the files if there # are conflicts (Merging from VS is not affected by the settings below, in VS # the diff markers are never inserted). Diff markers may cause the following # file extensions to fail to load in VS. An alternative would be to treat # these files as binary and thus will always conflict and require user # intervention with every merge. To do so, just uncomment the entries below ############################################################################### #*.sln merge=binary #*.csproj merge=binary #*.vbproj merge=binary #*.vcxproj merge=binary #*.vcproj merge=binary #*.dbproj merge=binary #*.fsproj merge=binary #*.lsproj merge=binary #*.wixproj merge=binary #*.modelproj merge=binary #*.sqlproj merge=binary #*.wwaproj merge=binary ############################################################################### # behavior for image files # # image files are treated as binary by default. ############################################################################### #*.jpg binary #*.png binary #*.gif binary ############################################################################### # diff behavior for common document formats # # Convert binary document formats to text before diffing them. This feature # is only available from the command line. Turn it on by uncommenting the # entries below. ############################################################################### #*.doc diff=astextplain #*.DOC diff=astextplain #*.docx diff=astextplain #*.DOCX diff=astextplain #*.dot diff=astextplain #*.DOT diff=astextplain #*.pdf diff=astextplain #*.PDF diff=astextplain #*.rtf diff=astextplain #*.RTF diff=astextplain ================================================ FILE: blog-samples/CSharp/ScorableBotSample/.gitignore ================================================ ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. # User-specific files *.suo *.user *.userosscache *.sln.docstates # User-specific files (MonoDevelop/Xamarin Studio) *.userprefs # Build results [Dd]ebug/ [Dd]ebugPublic/ [Rr]elease/ [Rr]eleases/ x64/ x86/ bld/ [Bb]in/ [Oo]bj/ [Ll]og/ # Visual Studio 2015 cache/options directory .vs/ # Uncomment if you have tasks that create the project's static files in wwwroot #wwwroot/ # MSTest test Results [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* # NUNIT *.VisualState.xml TestResult.xml # Build Results of an ATL Project [Dd]ebugPS/ [Rr]eleasePS/ dlldata.c # DNX project.lock.json project.fragment.lock.json artifacts/ *_i.c *_p.c *_i.h *.ilk *.meta *.obj *.pch *.pdb *.pgc *.pgd *.rsp *.sbr *.tlb *.tli *.tlh *.tmp *.tmp_proj *.log *.vspscc *.vssscc .builds *.pidb *.svclog *.scc # Chutzpah Test files _Chutzpah* # Visual C++ cache files ipch/ *.aps *.ncb *.opendb *.opensdf *.sdf *.cachefile *.VC.db *.VC.VC.opendb # Visual Studio profiler *.psess *.vsp *.vspx *.sap # TFS 2012 Local Workspace $tf/ # Guidance Automation Toolkit *.gpState # ReSharper is a .NET coding add-in _ReSharper*/ *.[Rr]e[Ss]harper *.DotSettings.user # JustCode is a .NET coding add-in .JustCode # TeamCity is a build add-in _TeamCity* # DotCover is a Code Coverage Tool *.dotCover # NCrunch _NCrunch_* .*crunch*.local.xml nCrunchTemp_* # MightyMoose *.mm.* AutoTest.Net/ # Web workbench (sass) .sass-cache/ # Installshield output folder [Ee]xpress/ # DocProject is a documentation generator add-in DocProject/buildhelp/ DocProject/Help/*.HxT DocProject/Help/*.HxC DocProject/Help/*.hhc DocProject/Help/*.hhk DocProject/Help/*.hhp DocProject/Help/Html2 DocProject/Help/html # Click-Once directory publish/ # Publish Web Output *.[Pp]ublish.xml *.azurePubxml # TODO: Comment the next line if you want to checkin your web deploy settings # but database connection strings (with potential passwords) will be unencrypted #*.pubxml *.publishproj # Microsoft Azure Web App publish settings. Comment the next line if you want to # checkin your Azure Web App publish settings, but sensitive information contained # in these scripts will be unencrypted PublishScripts/ # NuGet Packages *.nupkg # The packages folder can be ignored because of Package Restore **/packages/* # except build/, which is used as an MSBuild target. !**/packages/build/ # Uncomment if necessary however generally it will be regenerated when needed #!**/packages/repositories.config # NuGet v3's project.json files produces more ignoreable files *.nuget.props *.nuget.targets # Microsoft Azure Build Output csx/ *.build.csdef # Microsoft Azure Emulator ecf/ rcf/ # Windows Store app package directories and files AppPackages/ BundleArtifacts/ Package.StoreAssociation.xml _pkginfo.txt # Visual Studio cache files # files ending in .cache can be ignored *.[Cc]ache # but keep track of directories ending in .cache !*.[Cc]ache/ # Others ClientBin/ ~$* *~ *.dbmdl *.dbproj.schemaview *.jfm *.pfx *.publishsettings node_modules/ orleans.codegen.cs # Since there are multiple workflows, uncomment next line to ignore bower_components # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) #bower_components/ # RIA/Silverlight projects Generated_Code/ # Backup & report files from converting an old project file # to a newer Visual Studio version. Backup files are not needed, # because we have git ;-) _UpgradeReport_Files/ Backup*/ UpgradeLog*.XML UpgradeLog*.htm # SQL Server files *.mdf *.ldf # Business Intelligence projects *.rdl.data *.bim.layout *.bim_*.settings # Microsoft Fakes FakesAssemblies/ # GhostDoc plugin setting file *.GhostDoc.xml # Node.js Tools for Visual Studio .ntvs_analysis.dat # Visual Studio 6 build log *.plg # Visual Studio 6 workspace options file *.opt # Visual Studio LightSwitch build output **/*.HTMLClient/GeneratedArtifacts **/*.DesktopClient/GeneratedArtifacts **/*.DesktopClient/ModelManifest.xml **/*.Server/GeneratedArtifacts **/*.Server/ModelManifest.xml _Pvt_Extensions # Paket dependency manager .paket/paket.exe paket-files/ # FAKE - F# Make .fake/ # JetBrains Rider .idea/ *.sln.iml # CodeRush .cr/ # Python Tools for Visual Studio (PTVS) __pycache__/ *.pyc ================================================ FILE: blog-samples/CSharp/ScorableBotSample/README.md ================================================ # ScorableBot One of the features of using Dialogs is that it encourages developers to define a conversational hierarchy. This makes sense in some user scenarios where it is important to progress through a certain set of steps before reaching an outcome. It does, however have some limitations in that being explicit about the conversation hierarchy results in a conversation which is inflexible and often does not respond to the whim of the user. Scorables are a Bot Framework mechanism by which you can compose different parts of conversations without hardcoding the hierarchy. This composition allows a fluid user experience which is akin to a natural conversation The benefits of composing chatbot conversations in this way are: - Users can access different parts of the conversation without knowing the route to get there. - Users do not have to 'back track' conversations. This example demonstrates this using a 'Banking Bot' scenario, where a user may issue the command 'make payment' or 'check balance' at any point in the conversation. A developer wishing to implement scorable conversations should, 1. Provide 1 or more implementations of Microsoft.Bot.Builder.Scorables.Internals.ScorableBase. See ScorableCheckBalance.cs and ScorableMakePayment.cs for examples of this. 2. Make the Autofac IOC container aware of our scorable implementations. See Global.asax.cs for this. In practice, the Bot Framework runtime will: 1. For each scorable implementation: - Call HasScore() - If HasScore() is true, then call GetScore() 2. Compare the results of GetScore() from each scorable implementation 3. Call PostAsync() on highest scorable 4. Call DoneAsync() on all scorables ================================================ FILE: blog-samples/CSharp/ScorableBotSample/ScorableBot/App_Start/WebApiConfig.cs ================================================ using System.Web.Http; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; namespace ScorableTest { public static class WebApiConfig { public static void Register(HttpConfiguration config) { // Json settings config.Formatters.JsonFormatter.SerializerSettings.NullValueHandling = NullValueHandling.Ignore; config.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); config.Formatters.JsonFormatter.SerializerSettings.Formatting = Formatting.Indented; JsonConvert.DefaultSettings = () => new JsonSerializerSettings() { ContractResolver = new CamelCasePropertyNamesContractResolver(), Formatting = Newtonsoft.Json.Formatting.Indented, NullValueHandling = NullValueHandling.Ignore, }; // Web API configuration and services // Web API routes config.MapHttpAttributeRoutes(); config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{id}", defaults: new { id = RouteParameter.Optional } ); } } } ================================================ FILE: blog-samples/CSharp/ScorableBotSample/ScorableBot/Controllers/MessagesController.cs ================================================ using System; using System.Linq; using System.Net; using System.Net.Http; using System.Threading.Tasks; using System.Web.Http; using Microsoft.Bot.Builder.Dialogs; using Microsoft.Bot.Connector; using ScorableTest.Dialogs; namespace ScorableTest.Controllers { [BotAuthentication] public class MessagesController : ApiController { public async Task Post([FromBody] Activity activity) { if (activity.Type == ActivityTypes.ConversationUpdate && activity.MembersAdded.Any(m => m.Id == activity.Recipient.Id)) { var connector = new ConnectorClient(new Uri(activity.ServiceUrl)); var reply = activity.CreateReply($"[MessagesController] You can interrupt me with IScorable by saying 'check balance' or 'make payment' at any point. Otherwise, I will just echo back what you say to me!"); await connector.Conversations.ReplyToActivityAsync(reply); } else if (activity.Type == ActivityTypes.Message) { await Conversation.SendAsync(activity, () => new RootDialog()); } return Request.CreateResponse(HttpStatusCode.OK); } } } ================================================ FILE: blog-samples/CSharp/ScorableBotSample/ScorableBot/Dialogs/Balance/Current/CheckBalanceCurrentDialog.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using System.Web; using Microsoft.Bot.Builder.Dialogs; namespace ScorableTest.Dialogs.Balance.Current { [Serializable] public class CheckBalanceCurrentDialog : IDialog { // Entry point to the Dialog public async Task StartAsync(IDialogContext context) { await context.PostAsync("[CheckBalanceCurrentDialog] Your current account balance is £2000"); // State transition - complete this Dialog and remove it from the stack context.Done(new object()); } } } ================================================ FILE: blog-samples/CSharp/ScorableBotSample/ScorableBot/Dialogs/Balance/Savings/CheckBalanceSavingsDialog.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using System.Web; using Microsoft.Bot.Builder.Dialogs; namespace ScorableTest.Dialogs.Balance.Savings { [Serializable] public class CheckBalanceSavingsDialog : IDialog { // Entry point to the Dialog public async Task StartAsync(IDialogContext context) { await context.PostAsync("[CheckBalanceSavingsDialog] Your savings account balance is £5000"); // State transition - complete this Dialog and remove it from the stack context.Done(new object()); } } } ================================================ FILE: blog-samples/CSharp/ScorableBotSample/ScorableBot/Dialogs/Balance/ScorableCheckBalance.cs ================================================ using System.Threading; using System.Threading.Tasks; using Microsoft.Bot.Builder.Dialogs; using Microsoft.Bot.Builder.Dialogs.Internals; using Microsoft.Bot.Builder.Internals.Fibers; using Microsoft.Bot.Builder.Scorables.Internals; using Microsoft.Bot.Connector; namespace ScorableTest.Dialogs.Balance { public class ScorableCheckBalance : ScorableBase { private readonly IDialogStack stack; public ScorableCheckBalance(IDialogStack stack) { SetField.NotNull(out this.stack, nameof(stack), stack); } protected override Task DoneAsync(IActivity item, string state, CancellationToken token) { return Task.CompletedTask; } protected override double GetScore(IActivity item, string state) { return state != null && state == "scorable2-triggered" ? 1 : 0; } protected override bool HasScore(IActivity item, string state) { return state != null && state == "scorable2-triggered"; } protected override Task PostAsync(IActivity item, string state, CancellationToken token) { var message = item as IMessageActivity; var dialog = new ScorableCheckBalanceDialog(); var interruption = dialog.Void(stack); stack.Call(interruption, null); return Task.CompletedTask; } protected override async Task PrepareAsync(IActivity item, CancellationToken token) { var message = item.AsMessageActivity(); if (message == null) return null; var messageText = message.Text; return messageText == "check balance" ? "scorable2-triggered" : null; // this value is passed to GetScore/HasScore/PostAsync and can be anything meaningful to the scoring } } } ================================================ FILE: blog-samples/CSharp/ScorableBotSample/ScorableBot/Dialogs/Balance/ScorableCheckBalanceDialog.cs ================================================ using System; using System.Threading.Tasks; using Microsoft.Bot.Builder.Dialogs; using Microsoft.Bot.Connector; using ScorableTest.Dialogs.Balance.Current; using ScorableTest.Dialogs.Balance.Savings; namespace ScorableTest.Dialogs.Balance { [Serializable] public class ScorableCheckBalanceDialog : IDialog { // Entry point to the Dialog public async Task StartAsync(IDialogContext context) { await context.PostAsync("[ScorableCheckBalanceDialog] Which account - Current or Savings?"); context.Wait(MessageReceivedOperationChoice); } public async Task MessageReceivedOperationChoice(IDialogContext context, IAwaitable argument) { var message = await argument; if (message.Text.Equals("current", StringComparison.InvariantCultureIgnoreCase)) { // State transition - add 'current account' Dialog to the stack, when done call AfterChildDialogIsDone callback context.Call(new CheckBalanceCurrentDialog(), AfterChildDialogIsDone); } else if (message.Text.Equals("savings", StringComparison.InvariantCultureIgnoreCase)) { // State transition - add 'savings account' Dialog to the stack, when done call AfterChildDialogIsDone callback context.Call(new CheckBalanceSavingsDialog(), AfterChildDialogIsDone); } else { await context.PostAsync("[ScorableCheckBalanceDialog] Please repeat, which account - Current or Savings?"); // State transition - wait for 'operation choice' message from user (loop back) context.Wait(MessageReceivedOperationChoice); } } private async Task AfterChildDialogIsDone(IDialogContext context, IAwaitable result) { // State transition - complete this Dialog and remove it from the stack context.Done(new object()); } } } ================================================ FILE: blog-samples/CSharp/ScorableBotSample/ScorableBot/Dialogs/MakePayment/ScorableMakePayment.cs ================================================ using System.Threading; using System.Threading.Tasks; using Microsoft.Bot.Builder.Dialogs; using Microsoft.Bot.Builder.Dialogs.Internals; using Microsoft.Bot.Builder.Internals.Fibers; using Microsoft.Bot.Builder.Scorables.Internals; using Microsoft.Bot.Connector; namespace ScorableTest.Dialogs.MakePayment { public class ScorableMakePayment : ScorableBase { private readonly IDialogStack stack; public ScorableMakePayment(IDialogStack stack) { SetField.NotNull(out this.stack, nameof(stack), stack); } protected override Task DoneAsync(IActivity item, string state, CancellationToken token) { return Task.CompletedTask; } protected override double GetScore(IActivity item, string state) { return state != null && state == "scorable1-triggered" ? 1 : 0; } protected override bool HasScore(IActivity item, string state) { return state != null && state == "scorable1-triggered"; } protected override Task PostAsync(IActivity item, string state, CancellationToken token) { var message = item as IMessageActivity; var dialog = new ScorableMakePaymentDialog(); var interruption = dialog.Void(stack); stack.Call(interruption, null); return Task.CompletedTask; } protected override async Task PrepareAsync(IActivity item, CancellationToken token) { var message = item.AsMessageActivity(); if (message == null) return null; var messageText = message.Text; return messageText == "make payment" ? "scorable1-triggered" : null; // this value is passed to GetScore/HasScore/PostAsync and can be anything meaningful to the scoring } } } ================================================ FILE: blog-samples/CSharp/ScorableBotSample/ScorableBot/Dialogs/MakePayment/ScorableMakePaymentDialog.cs ================================================ using System; using System.Threading.Tasks; using Microsoft.Bot.Builder.Dialogs; using Microsoft.Bot.Connector; namespace ScorableTest.Dialogs { [Serializable] public class ScorableMakePaymentDialog : IDialog { protected string payee; protected string amount; // Entry point to the Dialog public async Task StartAsync(IDialogContext context) { await context.PostAsync($"[ScorableMakePaymentDialog] Who would you like to pay?"); // State transition - wait for 'payee' message from user context.Wait(MessageReceivedPayee); } public async Task MessageReceivedPayee(IDialogContext context, IAwaitable argument) { var message = await argument; this.payee = message.Text; await context.PostAsync($"[ScorableMakePaymentDialog] {this.payee}, got it{Environment.NewLine}How much should I pay?"); // State transition - wait for 'amount' message from user context.Wait(MessageReceivedAmount); } public async Task MessageReceivedAmount(IDialogContext context, IAwaitable argument) { var message = await argument; this.amount = message.Text; await context.PostAsync($"[ScorableMakePaymentDialog] Thank you, I've paid {this.amount} to {this.payee} 💸"); // State transition - complete this Dialog and remove it from the stack context.Done(new object()); } } } ================================================ FILE: blog-samples/CSharp/ScorableBotSample/ScorableBot/Dialogs/RootDialog.cs ================================================ using System; using System.Threading.Tasks; using Microsoft.Bot.Builder.Dialogs; using Microsoft.Bot.Connector; namespace ScorableTest.Dialogs { [Serializable] public class RootDialog : IDialog { public Task StartAsync(IDialogContext context) { context.PostAsync($"[RootDialog] I am the root dialog."); context.Wait(MessageReceivedAsync); return Task.CompletedTask; } private async Task MessageReceivedAsync(IDialogContext context, IAwaitable result) { var activity = await result as IMessageActivity; if (activity != null && activity.Type == ActivityTypes.Message) { int length = (activity.Text ?? string.Empty).Length; await context.PostAsync($"[RootDialog] You sent {activity.Text} which was {length} characters"); } context.Wait(MessageReceivedAsync); } } } ================================================ FILE: blog-samples/CSharp/ScorableBotSample/ScorableBot/Global.asax ================================================ <%@ Application Codebehind="Global.asax.cs" Inherits="ScorableTest.WebApiApplication" Language="C#" %> ================================================ FILE: blog-samples/CSharp/ScorableBotSample/ScorableBot/Global.asax.cs ================================================ using System.Web; using System.Web.Http; using Autofac; using Microsoft.Bot.Builder.Dialogs; using Microsoft.Bot.Builder.Scorables; using Microsoft.Bot.Connector; using ScorableTest.Dialogs.Balance; using ScorableTest.Dialogs.MakePayment; namespace ScorableTest { public class WebApiApplication : HttpApplication { protected void Application_Start() { GlobalConfiguration.Configure(WebApiConfig.Register); // register our scorables var builder = new ContainerBuilder(); builder.RegisterType() .As>() .InstancePerLifetimeScope(); builder.RegisterType() .As>() .InstancePerLifetimeScope(); builder.Update(Conversation.Container); } } } ================================================ FILE: blog-samples/CSharp/ScorableBotSample/ScorableBot/Properties/AssemblyInfo.cs ================================================ using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. [assembly: AssemblyTitle("ScorableTest")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("ScorableTest")] [assembly: AssemblyCopyright("Copyright © 2016")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] // Setting ComVisible to false makes the types in this assembly not visible // to COM components. If you need to access a type in this assembly from // COM, set the ComVisible attribute to true on that type. [assembly: ComVisible(false)] // The following GUID is for the ID of the typelib if this project is exposed to COM [assembly: Guid("f082bcd0-6b15-4f7a-b77d-21b1146734b0")] // Version information for an assembly consists of the following four values: // // Major Version // Minor Version // Build Number // Revision // // You can specify all the values or you can default the Revision and Build Numbers // by using the '*' as shown below: [assembly: AssemblyVersion("1.0.0.0")] [assembly: AssemblyFileVersion("1.0.0.0")] ================================================ FILE: blog-samples/CSharp/ScorableBotSample/ScorableBot/ScorableBot.csproj ================================================  Debug AnyCPU 2.0 {A8BA1066-5695-4D71-ABB4-65E5A5E0C3D4} {349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} Library Properties ScorableTest Bot Application v4.6 true true full false bin\ DEBUG;TRACE prompt 4 pdbonly true bin\ TRACE prompt 4 $(SolutionDir)\packages\Autofac.3.5.2\lib\net40\Autofac.dll $(SolutionDir)\packages\Chronic.Signed.0.3.2\lib\net40\Chronic.dll ..\packages\Microsoft.Bot.Builder.3.8.3.0\lib\net46\Microsoft.Bot.Builder.dll ..\packages\Microsoft.Bot.Builder.3.8.3.0\lib\net46\Microsoft.Bot.Builder.Autofac.dll ..\packages\Microsoft.Bot.Builder.3.8.3.0\lib\net46\Microsoft.Bot.Connector.dll ..\packages\Microsoft.IdentityModel.Protocol.Extensions.1.0.4.403061554\lib\net45\Microsoft.IdentityModel.Protocol.Extensions.dll $(SolutionDir)\packages\Microsoft.Rest.ClientRuntime.2.3.2\lib\net45\Microsoft.Rest.ClientRuntime.dll $(SolutionDir)\packages\Microsoft.WindowsAzure.ConfigurationManager.3.1.0\lib\net40\Microsoft.WindowsAzure.Configuration.dll $(SolutionDir)\packages\Newtonsoft.Json.8.0.3\lib\net45\Newtonsoft.Json.dll ..\packages\System.IdentityModel.Tokens.Jwt.4.0.4.403061554\lib\net45\System.IdentityModel.Tokens.Jwt.dll $(SolutionDir)\packages\Microsoft.AspNet.WebApi.Client.5.2.3\lib\net45\System.Net.Http.Formatting.dll $(SolutionDir)\packages\Microsoft.AspNet.WebApi.Core.5.2.3\lib\net45\System.Web.Http.dll $(SolutionDir)\packages\Microsoft.AspNet.WebApi.WebHost.5.2.3\lib\net45\System.Web.Http.WebHost.dll Designer Global.asax Web.config Web.config 10.0 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) true True True 3979 / http://localhost:3979/ False False False ================================================ FILE: blog-samples/CSharp/ScorableBotSample/ScorableBot/Web.Debug.config ================================================ ================================================ FILE: blog-samples/CSharp/ScorableBotSample/ScorableBot/Web.Release.config ================================================ ================================================ FILE: blog-samples/CSharp/ScorableBotSample/ScorableBot/Web.config ================================================  ================================================ FILE: blog-samples/CSharp/ScorableBotSample/ScorableBot/default.htm ================================================ 

ScorableTest

Describe your bot here and your terms of use etc.

Visit Bot Framework to register your bot. When you register it, remember to set your bot's endpoint to

https://your_bots_hostname/api/messages

================================================ FILE: blog-samples/CSharp/ScorableBotSample/ScorableBot/packages.config ================================================  ================================================ FILE: blog-samples/CSharp/ScorableBotSample/ScorableBotSample.sln ================================================  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 VisualStudioVersion = 15.0.26403.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ScorableBot", "ScorableBot\ScorableBot.csproj", "{A8BA1066-5695-4D71-ABB4-65E5A5E0C3D4}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {A8BA1066-5695-4D71-ABB4-65E5A5E0C3D4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A8BA1066-5695-4D71-ABB4-65E5A5E0C3D4}.Debug|Any CPU.Build.0 = Debug|Any CPU {A8BA1066-5695-4D71-ABB4-65E5A5E0C3D4}.Release|Any CPU.ActiveCfg = Release|Any CPU {A8BA1066-5695-4D71-ABB4-65E5A5E0C3D4}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection EndGlobal ================================================ FILE: blog-samples/CSharp/TriviaBotSpeechSample/.gitignore ================================================ ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. # User-specific files *.suo *.user *.userosscache *.sln.docstates # User-specific files (MonoDevelop/Xamarin Studio) *.userprefs # Build results [Dd]ebug/ [Dd]ebugPublic/ [Rr]elease/ [Rr]eleases/ [Xx]64/ [Xx]86/ [Bb]uild/ bld/ [Bb]in/ [Oo]bj/ Generated Files/ # Visual Studio 2015 cache/options directory .vs/ # Uncomment if you have tasks that create the project's static files in wwwroot #wwwroot/ # MSTest test Results [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* # NUNIT *.VisualState.xml TestResult.xml # Build Results of an ATL Project [Dd]ebugPS/ [Rr]eleasePS/ dlldata.c # DNX *project.lock.json artifacts/ *_i.c *_p.c *_i.h *.ilk *.meta *.obj *.pch *.pdb *.pgc *.pgd *.rsp *.sbr *.tlb *.tli *.tlh *.tmp *.tmp_proj *.log *.vspscc *.vssscc .builds *.pidb *.svclog *.scc # Chutzpah Test files _Chutzpah* # Visual C++ cache files ipch/ *.aps *.ncb *.opendb *.opensdf *.sdf *.cachefile *.VC.db # Visual Studio profiler *.psess *.vsp *.vspx *.sap # TFS 2012 Local Workspace $tf/ # Guidance Automation Toolkit *.gpState # ReSharper is a .NET coding add-in _ReSharper*/ *.[Rr]e[Ss]harper *.DotSettings.user # JustCode is a .NET coding add-in .JustCode # TeamCity is a build add-in _TeamCity* # DotCover is a Code Coverage Tool *.dotCover # NCrunch _NCrunch_* .*crunch*.local.xml nCrunchTemp_* # MightyMoose *.mm.* AutoTest.Net/ # Web workbench (sass) .sass-cache/ # Installshield output folder [Ee]xpress/ # DocProject is a documentation generator add-in DocProject/buildhelp/ DocProject/Help/*.HxT DocProject/Help/*.HxC DocProject/Help/*.hhc DocProject/Help/*.hhk DocProject/Help/*.hhp DocProject/Help/Html2 DocProject/Help/html # Click-Once directory publish/ # Publish Web Output *.[Pp]ublish.xml *.azurePubxml # TODO: Un-comment the next line if you do not want to checkin # your web deploy settings because they may include unencrypted # passwords #*.pubxml *.publishproj # NuGet Packages *.nupkg # The packages folder can be ignored because of Package Restore **/packages/* # except build/, which is used as an MSBuild target. !**/packages/build/ # Uncomment if necessary however generally it will be regenerated when needed #!**/packages/repositories.config # NuGet v3's project.json files produces more ignoreable files *.nuget.props *.nuget.targets # Microsoft Azure Build Output csx/ *.build.csdef # Microsoft Azure Emulator ecf/ rcf/ # Windows Store app package directory AppPackages/ BundleArtifacts/ # Visual Studio cache files # files ending in .cache can be ignored *.[Cc]ache # but keep track of directories ending in .cache !*.[Cc]ache/ # Others ClientBin/ ~$* *~ *.dbmdl *.dbproj.schemaview *.pfx *.publishsettings node_modules/ orleans.codegen.cs # RIA/Silverlight projects Generated_Code/ # Backup & report files from converting an old project file # to a newer Visual Studio version. Backup files are not needed, # because we have git ;-) _UpgradeReport_Files/ Backup*/ UpgradeLog*.XML UpgradeLog*.htm # SQL Server files *.mdf *.ldf # Business Intelligence projects *.rdl.data *.bim.layout *.bim_*.settings # Microsoft Fakes FakesAssemblies/ # GhostDoc plugin setting file *.GhostDoc.xml # Node.js Tools for Visual Studio .ntvs_analysis.dat # Visual Studio 6 build log *.plg # Visual Studio 6 workspace options file *.opt # Visual Studio LightSwitch build output **/*.HTMLClient/GeneratedArtifacts **/*.DesktopClient/GeneratedArtifacts **/*.DesktopClient/ModelManifest.xml **/*.Server/GeneratedArtifacts **/*.Server/ModelManifest.xml _Pvt_Extensions # LightSwitch generated files GeneratedArtifacts/ ModelManifest.xml # Paket dependency manager .paket/paket.exe # FAKE - F# Make .fake/ /BotFrameworkHost/Properties/PublishProfiles/Bot-33A768ED-4803-44E6-814B-9DDC90FCECF3 - Web Deploy.pubxml /CafeBotv2/BotData /RadeAgent2RTree # BingSpeechBindings downloaded native code SpeechSDK.framework/ SpeechSDK.framework* libandroid_platform.so ================================================ FILE: blog-samples/CSharp/TriviaBotSpeechSample/LICENSE ================================================ MIT License Copyright (c) 2017 Maxwell Miller 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. ================================================ FILE: blog-samples/CSharp/TriviaBotSpeechSample/README.md ================================================ # TriviaBotSpeechSample A sample trivia bot, leveraging the Microsoft Bot Framework and https://opentdb.com/ that showcases the use of the new speech-enabled Microsoft.Bot.Client NuGet package and Microsoft Bot Framework C# Builder SDK features. This sample contains two projects, a trivia bot built on top of the Microsoft Bot Framework C# Builder SDK, and a UWP app that talks to the bot using the Microsoft.Bot.Client NuGet package. As this is a combined client/sample demo, there is a bit of setup required. Setup: 1) Register a bot with the Bot Framework at http://dev.botframework.com/ and add the AppId and AppPassword to TriviaBot\Web.config. 2) Enable the Direct Line channel in the bot settings page, Add a new site and paste a Direct Line secret in TriviaApp\BotConnection.cs. 3) Create a LUIS app on http://luis.ai/ and import TriviaBotLU.json as a new app. Train and publish the model, and add the app id and subscription key in TriviaBot\TriviaDialog.cs (there is an error pointing to the location). The LUIS app id and subscription key can be extracted from the Endpoint Url provided on the "Publish App" page at http://luis.ai/ The link format is: https://westus.api.cognitive.microsoft.com/luis/v2.0/apps/yourappid?subscription-key=yoursubscriptionkey&verbose=true&q=. 4) Publish the Bot as an Azure App Service, and add the public endpoint (yourhosturl/api/messages) to the Bot Framework portal settings page. Make sure to use https instead of http in the url. 5) [Optional] To improve speech recognition for your bot: On http://dev.botframework.com/ go to the bot's settings. In the "Speech recognition priming with LUIS" section you should see a list of LUIS apps associated with the account you are logged in with. Check the new LUIS app you created for this bot and hit save. This information is used to improve speech recognition when you speak to this bot and uses the Cognitive Speech apis for speech recognition. Speech recognition priming improves the recognition accuracy for the utterances and entities defined in your LUIS app for this bot. To start a conversation with this bot, you can say something like "let's play trivia" or "let's play geography trivia" You can talk to your new bot in multiple ways. Here are some options to try, all of which support speech input and output: 1) Using the TriviaApp included in this sample. Simply hit F5 in Visual Studio 2) Using the bot framework emulator https://docs.microsoft.com/en-us/bot-framework/debug-bots-emulator. 2) Host your own instance of the Bot Framework WebChat client: https://aka.ms/BfWebChat 3) Enabled your bot as a Cortana Skill. Simply enable the Cortana channel and provide an invocation phrase. Then make sure you are logged in to cortana using the same microsoft account, and say "Ask to start a game of trivia". Cortana should trigger your bot! ================================================ FILE: blog-samples/CSharp/TriviaBotSpeechSample/TriviaApp/App.xaml ================================================  ================================================ FILE: blog-samples/CSharp/TriviaBotSpeechSample/TriviaApp/App.xaml.cs ================================================ using System; using System.Linq; using Windows.ApplicationModel; using Windows.ApplicationModel.Activation; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Navigation; namespace TrivaApp { /// /// Provides application-specific behavior to supplement the default Application class. /// sealed partial class App : Application { /// /// Initializes the singleton application object. This is the first line of authored code /// executed, and as such is the logical equivalent of main() or WinMain(). /// public App() { this.InitializeComponent(); this.Suspending += OnSuspending; } private Frame HandleCommonActivation(IActivatedEventArgs args) { Frame rootFrame = Window.Current.Content as Frame; // Do not repeat app initialization when the Window already has content, // just ensure that the window is active if (rootFrame == null) { // Create a Frame to act as the navigation context and navigate to the first page rootFrame = new Frame(); rootFrame.NavigationFailed += OnNavigationFailed; if (args.PreviousExecutionState == ApplicationExecutionState.Terminated) { //TODO: Load state from previously suspended application } // Place the frame in the current Window Window.Current.Content = rootFrame; } return rootFrame; } /// /// Invoked when the application is launched normally by the end user. Other entry points /// will be used such as when the application is launched to open a specific file. /// /// Details about the launch request and process. protected override void OnLaunched(LaunchActivatedEventArgs args) { #if DEBUG if (System.Diagnostics.Debugger.IsAttached) { this.DebugSettings.EnableFrameRateCounter = true; } #endif var rootFrame = HandleCommonActivation(args); if (args.PrelaunchActivated == false) { if (rootFrame.Content == null) { // When the navigation stack isn't restored navigate to the first page, // configuring the new page by passing required information as a navigation // parameter rootFrame.Navigate(typeof(MainPage), args.Arguments); } // TODO: If you want to launch fullscreen, uncomment this line. //ApplicationView.PreferredLaunchWindowingMode = ApplicationViewWindowingMode.FullScreen; // Ensure the current window is active Window.Current.Activate(); } base.OnLaunched(args); } protected override void OnActivated(IActivatedEventArgs args) { #if DEBUG if (System.Diagnostics.Debugger.IsAttached) { this.DebugSettings.EnableFrameRateCounter = true; } #endif if (args.Kind == ActivationKind.Protocol) { var rootFrame = HandleCommonActivation(args); var eventArgs = (ProtocolActivatedEventArgs)args; var uri = eventArgs.Uri; var linkData = uri.AbsolutePath.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries); if (linkData.Count() > 0 && uri.Host == "play" && linkData[0] == "gameshow") { rootFrame.Navigate(typeof(MainPage), "gameshow-start"); } // Ensure the current window is active Window.Current.Activate(); } } /// /// Invoked when Navigation to a certain page fails /// /// The Frame which failed navigation /// Details about the navigation failure void OnNavigationFailed(object sender, NavigationFailedEventArgs e) { throw new Exception("Failed to load Page " + e.SourcePageType.FullName); } /// /// Invoked when application execution is being suspended. Application state is saved /// without knowing whether the application will be terminated or resumed with the contents /// of memory still intact. /// /// The source of the suspend request. /// Details about the suspend request. private void OnSuspending(object sender, SuspendingEventArgs e) { var deferral = e.SuspendingOperation.GetDeferral(); //TODO: Save application state and stop any background activity deferral.Complete(); } } } ================================================ FILE: blog-samples/CSharp/TriviaBotSpeechSample/TriviaApp/BotConnection.cs ================================================ // Copyright (c) Microsoft Corporation. All rights reserved. namespace TrivaApp { public static class BotConnection { #error You must specify the direct line secret of the bot to talk to #error This requires first publishing the TriviaBot project and configuring it on https://dev.botframework.com/ public static string DirectLineSecret { get; } = "Your DirectLine secret here"; #error Please provide a Bing Speech API key, if the Cognitive Services speech recognizer or synthesizer are used." public static string BingSpeechKey { get; } = "Your Bing Speech API key here"; public static string ApplicationName { get; } = "TriviaApp"; } } ================================================ FILE: blog-samples/CSharp/TriviaBotSpeechSample/TriviaApp/Converters/BoolToVisibilityConverter.cs ================================================ // Copyright (c) Microsoft Corporation. All rights reserved. using System; using Windows.UI.Xaml; using Windows.UI.Xaml.Data; namespace TrivaApp.Converters { public class BoolToVisibilityConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, string language) { if (parameter is string && (parameter as string).Equals("invert", StringComparison.CurrentCultureIgnoreCase)) { if (value is bool) { value = !(bool)value; } } if (value is bool && (bool)value) { return Visibility.Visible; } return Visibility.Collapsed; } public object ConvertBack(object value, Type targetType, object parameter, string language) { throw new NotImplementedException(); } } } ================================================ FILE: blog-samples/CSharp/TriviaBotSpeechSample/TriviaApp/MainPage.xaml ================================================  ================================================ FILE: blog-samples/CSharp/TriviaBotSpeechSample/TriviaApp/MainPage.xaml.cs ================================================ using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Linq; using System.Threading.Tasks; using Microsoft.Bot.Client.SpeechRecognition; using Microsoft.Bot.Client.SpeechSynthesis; using Microsoft.Bot.Connector.DirectLine; using TrivaApp.ViewModels; using TriviaBot.Shared; using Windows.Foundation; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Input; using Windows.UI.Xaml.Navigation; namespace TrivaApp { /// /// An empty page that can be used on its own or navigated to within a Frame. /// public sealed partial class MainPage : Page { private const string IdleSuggestionText = "Type or tap mic and speak."; private const string ListeningText = "Listening..."; private const string SpeechRecognitionTriggerPhrase = "hey trivia bot"; private const string BotNameTag = "🤖"; private const string UserNameTag = "👤"; private Microsoft.Bot.Client.BotClient _botClient = null; private bool _isListening; private bool _textAddedToBottom; private Task _startConversationTask; private ObservableCollection _chatCards = new ObservableCollection(); private ObservableCollection _answerCards = new ObservableCollection(); private CountdownTimer _countdownTimer = new CountdownTimer(); private bool _startCountdownTimer; private int _wrongAnswers; private int _rightAnswers; private bool _lightningMode = false; public MainPage() { this.InitializeComponent(); var windowsSpeechRecognizer = new WindowsSpeechRecognizer(); // Create the client. By default, it will poll the REST endpoint provided by the direct line, but optionally, we can give it a websocket implementation to use _botClient = new Microsoft.Bot.Client.BotClient(BotConnection.DirectLineSecret, BotConnection.ApplicationName) { // Use the speech synthesizer implementation in the WinRT Windows.Media.SpeechSynthesis namespace // Any voice supported by the API can be used. See this page as a reference: https://docs.microsoft.com/en-us/azure/cognitive-services/speech/api-reference-rest/bingvoiceoutput // The Built-in Windows speech synthesizer can be used here as an alternative, for a free solution: // SpeechSynthesizer = new WindowsSpeechSynthesizer(), SpeechSynthesizer = new CognitiveServicesSpeechSynthesizer(BotConnection.BingSpeechKey, Microsoft.Bot.Client.SpeechSynthesis.CognitiveServices.VoiceNames.Jessa_EnUs), // Use the Cognitive Services Speech-To-Text API, with speech priming support, as the speech recognizer // The Built-in WindowsSpeechRecognizer can be used here as an alternative, for a free solution: // SpeechRecognizer = windowsSpeechRecognizer, SpeechRecognizer = new CognitiveServicesSpeechRecognizer(BotConnection.BingSpeechKey), // Give us the ability to trigger speech recognition on keywords // The WindowsMediaSpeechRecognizer can also be used as the primary speech recognizer, instead of CognitiveServicesSpeechRecognizer (above) // for a free solution. TriggerRecognizer = windowsSpeechRecognizer }; // Attach to the callbacks the client provides for observing the state of the bot // This will be called every time the bot sends down an activity _botClient.ConversationUpdated += OnConversationUpdated; // Speech-related events _botClient.SpeechRecognitionStarted += OnSpeechRecognitionStarted; _botClient.IntermediateSpeechRecognitionResultReceived += OnIntermediateSpeechRecognitionResultReceived; _botClient.SpeechRecognitionEnded += OnSpeechRecognitionEnded; _botClient.FinalSpeechRecognitionResultReceived += OnFinalSpeechRecognitionResultReceived; _botClient.SpeechSynthesisEnded += OnSpeechSynthesisEnded; // Set triggers, so that, when the user says "listen" or "what is" the bot client will start speech recognition _botClient.SetStartSpeechRecognitionTriggers(new string[] { "listen", "trivia bot" }); _countdownTimer.PropertyChanged += UpdateCountdown; // Kick off the conversation _startConversationTask = _botClient.StartConversation(); } protected override async void OnNavigatedTo(NavigationEventArgs e) { if (e.Parameter is string && (string)e.Parameter == "gameshow-start") { _model["GameFormat"] = 0; var entities = new List { new Microsoft.Bot.Connector.DirectLine.Entity("skipIntro") }; await _botClient.SendMessageToBot("play in lightning mode", entities); } } private ObservableDictionary _model = new ObservableDictionary { { "GameFormat", 1 }, { "HypothesisText", "play trivia" }, { "SuggestionText", IdleSuggestionText }, { "LastBotMessage", string.Empty }, { "RemainingTime", "∞" }, { "EnableUserInput", true }, { "Score0", "0" }, { "Score0Label", "INCORRECT" }, { "Score1", "0" }, { "Score1Label", "CORRECT" }, { "ShowHypothesisText", false }, { "ShowGotItRight", false }, { "ShowGotItWrong", false }, { "ShowCategoryAll", true }, { "ShowCategoryFilm", false }, { "ShowCategoryAnimals", false }, { "ShowCategoryScience", false }, { "ShowCategoryGeography", false }, { "ShowCategoryMusic", false }, { "ShowCategoryArt", false } }; private readonly string[] _categories = new string[] { "All", "Film", "Animals", "Science", "Geography", "Music", "Art" }; public ObservableDictionary DefaultViewModel { get { return _model; } } private enum MessageSource { User, Bot } private IAsyncAction RunOnUi(Windows.UI.Core.DispatchedHandler action) { return Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, action); } private async void OnSpeechRecognitionStarted(object sender, EventArgs e) => await RunOnUi(async () => { await _botClient.SpeechSynthesizer.StopSpeakingAsync(); _isListening = true; _model["HypothesisText"] = string.Empty; _model["SuggestionText"] = ListeningText; _model["ShowHypothesisText"] = true; }); private async void OnIntermediateSpeechRecognitionResultReceived(object sender, SpeechRecognitionResultEventArgs e) => await RunOnUi(() => { if (e.Result.Status == SpeechRecognitionStatus.Success) { _model["HypothesisText"] = e?.Result?.Text ?? string.Empty; } }); private async void OnSpeechRecognitionEnded(object sender, EventArgs e) => await RunOnUi(() => { _model["HypothesisText"] = string.Empty; _model["SuggestionText"] = IdleSuggestionText; _model["ShowHypothesisText"] = false; _isListening = false; }); private async void OnFinalSpeechRecognitionResultReceived(object sender, SpeechRecognitionResultEventArgs e) => await RunOnUi(async () => { if (e.Result.Status == SpeechRecognitionStatus.Success && e.Result.Text?.Length > 0) { AddChatMessage(MessageSource.User, e.Result.Text); await _botClient.SendMessageToBot(e.Result.Text); } }); private async void OnConversationUpdated(object sender, Activity e) => await RunOnUi(() => { var appEntities = (AppEntities)null; // Don't do anything for messages this client sent if (e.From.Id == _botClient.ClientID) { return; } if (e.Entities != null) { appEntities = (from entity in e.Entities where entity.Type == "AppEntities" select entity).FirstOrDefault()?.GetAs(); if (appEntities != null) { // Parse message type if (appEntities.MessageType != null) { var messageType = appEntities.MessageType; _model["ShowGotItRight"] = false; _model["ShowGotItWrong"] = false; if (messageType != MessageType.Question) { _answerCards.Clear(); UpdateAnswerCards(); _countdownTimer.Reset(); _model["RemainingTime"] = "∞"; switch (messageType) { case MessageType.Statement: break; case MessageType.GotItRight: ++_rightAnswers; _model["Score1"] = _rightAnswers; _model["ShowGotItRight"] = true; break; case MessageType.GotItWrong: ++_wrongAnswers; _model["Score0"] = _wrongAnswers; _model["ShowGotItWrong"] = true; break; case MessageType.StartLightningMode: _lightningMode = true; break; case MessageType.StopLightningMode: _lightningMode = false; break; } } } } } if (!string.IsNullOrEmpty(e.Text)) { var choicesMessage = string.Empty; // Parse choices if (appEntities?.MessageType == MessageType.Question) { // We've received a question from the bot. Tell the bot client that the user is likely to say one of // these options, so it can specifically listen for them (supported in the Microsoft.Bot.Client.Universal.WindowsMediaSpeechRecognizer) _botClient.ListenFor(appEntities.TriviaAnswerOptions); _answerCards.Clear(); var index = 1; foreach (var option in appEntities.TriviaAnswerOptions ?? Enumerable.Empty()) { _answerCards.Add(new AnswerCard { Index = $"{index}.", AnswerText = $" {option}" }); choicesMessage += $"\n {index}. {option}"; ++index; } UpdateAnswerCards(); _startCountdownTimer = true; } _model["LastBotMessage"] = e.Text; AddChatMessage(MessageSource.Bot, e.Text + choicesMessage); } }); private void UpdateAnswerCards() { _model["AnswerCards"] = _answerCards; _model["ShowAnswerCards"] = _answerCards.Count > 0; } private async void OnSpeechSynthesisEnded(object sender, EventArgs e) => await RunOnUi(() => { if (_startCountdownTimer) { if (_lightningMode) { _countdownTimer.Start(TimeSpan.FromSeconds(10)); } _startCountdownTimer = false; } }); private async void UpdateCountdown(object sender, PropertyChangedEventArgs e) { var timeLeft = _countdownTimer.RemainingTime; if (timeLeft.TotalMilliseconds == 0) { _countdownTimer.Reset(); _model["RemainingTime"] = "∞"; var appEntities = new AppEntities { MessageType = MessageType.OutOfTime }; var dlEntity = new Microsoft.Bot.Connector.DirectLine.Entity(); dlEntity.SetAs(appEntities); // Send an entity to the bot to tell it that the user has run out of time. await _botClient.SendMessageToBot(null, new[] { dlEntity }); } else { _model["RemainingTime"] = timeLeft.TotalSeconds.ToString("0.00"); } } private void AddChatMessage(MessageSource messageSource, string message) { _textAddedToBottom = true; var newCard = new ChatCard(); if (messageSource == MessageSource.Bot) { newCard.Message = BotNameTag + "\n" + message; newCard.BorderAlignment = HorizontalAlignment.Left; } else if (messageSource == MessageSource.User) { newCard.Message = UserNameTag + "\n" + message; newCard.BorderAlignment = HorizontalAlignment.Right; } _chatCards.Add(newCard); _model["ChatCards"] = _chatCards; } private async void TextBox_KeyPressedEventHandler(object sender, KeyRoutedEventArgs e) { if (e.Key == Windows.System.VirtualKey.Enter) { string s = textBox.Text; textBox.Text = string.Empty; _model["HypothesisText"] = string.Empty; if (!string.IsNullOrWhiteSpace(s)) { AddChatMessage(MessageSource.User, s); // Send the user's message to the bot await _botClient.SendMessageToBot(s); } } } private void StackPanel_LayoutUpdated(object sender, object e) { if (_textAddedToBottom) { ScrollView.ChangeView(null, double.MaxValue, null); _textAddedToBottom = false; } } private void MicButton_Click(object sender, RoutedEventArgs e) { if (_isListening) { _botClient.AutoSpeechRecognition = false; _botClient.CancelSpeechRecognition(); } else { _botClient.StartSpeechRecognition(); _botClient.AutoSpeechRecognition = true; } } private async void AnswerOptions_SelectionChanged(object sender, SelectionChangedEventArgs e) { var selectedIndex = AnswerOptions.SelectedIndex; if (selectedIndex >= 0 && selectedIndex < _answerCards.Count) { _botClient.AutoSpeechRecognition = false; await _botClient.CancelSpeechSynthesis(); var choice = _answerCards[selectedIndex].AnswerText; _answerCards.Clear(); _model["AnswerCards"] = _answerCards; AddChatMessage(MessageSource.User, choice); await _botClient.SendMessageToBot(choice); } } private async Task SetCategory(string category) { // If the user clicked a category, stop any kind of speech input/output and just send the // category to the bot. await _botClient.SpeechRecognizer.CancelRecognitionAsync(); await _botClient.SpeechSynthesizer.StopSpeakingAsync(); await _botClient.SendMessageToBot("switch to category " + category); foreach (var c in _categories) { var visibilityFlag = "ShowCategory" + c; var visibility = c == category; if (!_model.ContainsKey(visibilityFlag) || (bool)_model[visibilityFlag] != visibility) { _model[visibilityFlag] = visibility; } } } private async void CategoryFilm_Click(object sender, RoutedEventArgs e) { await SetCategory("Film"); } private async void CategoryAnimals_Click(object sender, RoutedEventArgs e) { await SetCategory("Animals"); } private async void CategoryScience_Click(object sender, RoutedEventArgs e) { await SetCategory("Science"); } private async void CategoryAll_Click(object sender, RoutedEventArgs e) { await SetCategory("All"); } private async void CategoryGeography_Click(object sender, RoutedEventArgs e) { await SetCategory("Geography"); } private async void CategoryMusic_Click(object sender, RoutedEventArgs e) { await SetCategory("Music"); } private async void CategoryArt_Click(object sender, RoutedEventArgs e) { await SetCategory("Art"); } private void LightningModeButton_Click(object sender, RoutedEventArgs e) { _botClient.CancelSpeechSynthesis(); _botClient.CancelSpeechRecognition(); if (_lightningMode) { _botClient?.SendMessageToBot("stop lightning mode"); } else { _botClient?.SendMessageToBot("start lightning mode"); } } private void Pivot_SelectionChanged(object sender, SelectionChangedEventArgs e) { // The gameshow view showcases speech input and output, // while the chat view showcases a simple chat control. // Enable automatic speech interactions only when on the // gameshow view. UpdateAnswerCards(); if (ViewPivot.SelectedIndex == 0) { _botClient.AutoSpeechRecognition = true; _botClient.AutoSpeechSynthesis = true; } else { _botClient.AutoSpeechRecognition = false; _botClient.AutoSpeechSynthesis = false; _botClient.CancelSpeechSynthesis(); } } } } ================================================ FILE: blog-samples/CSharp/TriviaBotSpeechSample/TriviaApp/Package.appxmanifest ================================================  TriviaBotApp Microsoft Assets\StoreLogo.png Trivia App ================================================ FILE: blog-samples/CSharp/TriviaBotSpeechSample/TriviaApp/Properties/AssemblyInfo.cs ================================================ using System.Reflection; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. [assembly: AssemblyTitle("TrivaApp")] [assembly: AssemblyDescription("A speech-enabled trivia client app that is driven by a bot.")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("TriviaApp")] [assembly: AssemblyCopyright("Copyright © 2017")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] // Version information for an assembly consists of the following four values: // // Major Version // Minor Version // Build Number // Revision // // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("1.0.0.0")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: ComVisible(false)] ================================================ FILE: blog-samples/CSharp/TriviaBotSpeechSample/TriviaApp/Properties/Default.rd.xml ================================================ ================================================ FILE: blog-samples/CSharp/TriviaBotSpeechSample/TriviaApp/TriviaApp.csproj ================================================  Debug x86 {59DCDA8D-3407-4585-9B70-BBAD7DA51694} AppContainerExe Properties TriviaBotApp TriviaBotApp en-US UAP 10.0.14393.0 10.0.10586.0 14 512 {A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} TriviaBotApp_TemporaryKey.pfx True Always x86|x64|arm 9D2C610F90B8324AC7A66E9C602E25CAD0D678C1 true bin\x86\Debug\ DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP ;2008 full x86 false prompt true bin\x86\Release\ TRACE;NETFX_CORE;WINDOWS_UWP true ;2008 pdbonly x86 false prompt true true true bin\ARM\Debug\ DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP ;2008 full ARM false prompt true bin\ARM\Release\ TRACE;NETFX_CORE;WINDOWS_UWP true ;2008 pdbonly ARM false prompt true true true bin\x64\Debug\ DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP ;2008 full x64 false prompt true bin\x64\Release\ TRACE;NETFX_CORE;WINDOWS_UWP true ;2008 pdbonly x64 false prompt true true Designer Shared\AppEntities.cs Shared\MessageType.cs App.xaml MainPage.xaml Designer MSBuild:Compile Designer MSBuild:Compile Designer 14.0 ================================================ FILE: blog-samples/CSharp/TriviaBotSpeechSample/TriviaApp/ViewModels/AnswerCard.cs ================================================ namespace TrivaApp.ViewModels { internal class AnswerCard { public string Index { get; set; } public string AnswerText { get; set; } } } ================================================ FILE: blog-samples/CSharp/TriviaBotSpeechSample/TriviaApp/ViewModels/ChatCard.cs ================================================ using Windows.UI.Xaml; namespace TrivaApp.ViewModels { internal class ChatCard { public HorizontalAlignment BorderAlignment { get; set; } public string Message { get; set; } } } ================================================ FILE: blog-samples/CSharp/TriviaBotSpeechSample/TriviaApp/ViewModels/CountdownTimer.cs ================================================ using System; using System.ComponentModel; using Windows.UI.Xaml; namespace TrivaApp.ViewModels { internal class CountdownTimer : INotifyPropertyChanged { private DispatcherTimer _timer; private DateTime _endTime; public CountdownTimer() { _timer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(100) }; _endTime = DateTime.UtcNow; _timer.Tick += (sender, e) => { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("TimeRemaining")); }; } public TimeSpan RemainingTime { get { var remainingTime = _endTime - DateTime.UtcNow; return remainingTime.TotalMilliseconds < 0 ? TimeSpan.FromMilliseconds(0) : remainingTime; } } public void Start(TimeSpan remainingTime) { _endTime = DateTime.UtcNow + remainingTime; _timer.Start(); } public void Reset() { _timer.Stop(); _endTime = DateTime.UtcNow; } public event PropertyChangedEventHandler PropertyChanged; } } ================================================ FILE: blog-samples/CSharp/TriviaBotSpeechSample/TriviaApp/ViewModels/ObservableDictionary.cs ================================================ // Copyright (c) Microsoft Corporation. All rights reserved. using System.Collections.Generic; using System.Linq; using Windows.Foundation.Collections; namespace TrivaApp { /// /// Implementation of IObservableMap that supports reentrancy for use as a default view /// model. /// public class ObservableDictionary : IObservableMap { /// /// The underlying dictionary to build on /// private Dictionary _dictionary = new Dictionary(); /// /// Triggers when the map is changed /// public event MapChangedEventHandler MapChanged; /// /// Gets the keys that exist in this map /// public ICollection Keys { get { return _dictionary.Keys; } } /// /// Gets the values in this dictionary /// public ICollection Values { get { return _dictionary.Values; } } /// /// Gets how many elements are in the dictionary /// public int Count { get { return _dictionary.Count; } } /// /// Gets a value indicating whether this dictionary is readonly or not /// public bool IsReadOnly { get { return false; } } /// /// Gets and sets elements in this dictionary using the [] indexer /// /// The key value to access /// The object that is identified by key public object this[string key] { get { return _dictionary[key]; } set { _dictionary[key] = value; InvokeMapChanged(CollectionChange.ItemChanged, key); } } /// /// Does this dictionary contain item /// /// The element to look up /// true if the dictionary contains item, otherwise false public bool Contains(KeyValuePair item) { return _dictionary.Contains(item); } /// /// Does this dictionary contain key /// /// The key to lookup in the dictionary /// true if the dictionary contains key, otherwise false public bool ContainsKey(string key) { return _dictionary.ContainsKey(key); } /// /// Looks up key in the dictionary and sets value to the result /// /// The key to look up /// The reference to store the result in /// true if successful, otherwise false public bool TryGetValue(string key, out object value) { return _dictionary.TryGetValue(key, out value); } /// /// Adds the key/value pair to the dictionary /// /// The key to use /// The value to store public void Add(string key, object value) { _dictionary.Add(key, value); InvokeMapChanged(CollectionChange.ItemInserted, key); } /// /// Adds the key/value pair to the dictionary /// /// The key/value pair to store public void Add(KeyValuePair item) { Add(item.Key, item.Value); } /// /// Removes the key and its associated value from the dictionary /// /// The key to remove /// true if key existed, otherwise false public bool Remove(string key) { if (_dictionary.Remove(key)) { InvokeMapChanged(CollectionChange.ItemRemoved, key); return true; } return false; } /// /// Removes the key/value pair from the dictionary /// /// The key/value pair to remove /// true if the key/value pair existed in the dictionary public bool Remove(KeyValuePair item) { object currentValue; if (_dictionary.TryGetValue(item.Key, out currentValue) && object.Equals(item.Value, currentValue) && _dictionary.Remove(item.Key)) { InvokeMapChanged(CollectionChange.ItemRemoved, item.Key); return true; } return false; } /// /// Removes all key/value pairs from this dictionary /// public void Clear() { var priorKeys = _dictionary.Keys.ToArray(); _dictionary.Clear(); foreach (var key in priorKeys) { InvokeMapChanged(CollectionChange.ItemRemoved, key); } } /// /// Gets an enumerator for this dictionary /// /// The enumerator public IEnumerator> GetEnumerator() { return _dictionary.GetEnumerator(); } /// /// Gets an enumerator for this dictionary /// /// The enumerator System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return _dictionary.GetEnumerator(); } /// /// Copy all elements from this dictionary into array at the specified index /// /// The array to copy elements into /// The starting index to copy elements at public void CopyTo(KeyValuePair[] array, int arrayIndex) { int arraySize = array.Length; foreach (var pair in _dictionary) { if (arrayIndex >= arraySize) { break; } array[arrayIndex++] = pair; } } /// /// Triggers when a key is changed in the map /// /// How the key was changed /// What key was changed private void InvokeMapChanged(CollectionChange change, string key) { MapChanged?.Invoke(this, new ObservableDictionaryChangedEventArgs(change, key)); } /// /// The event args containing the key that was changed, and how it was changed, when a change takes place /// private class ObservableDictionaryChangedEventArgs : IMapChangedEventArgs { /// /// Initializes a new instance of the class. /// /// How the key was changed /// What key was changed public ObservableDictionaryChangedEventArgs(CollectionChange change, string key) { CollectionChange = change; Key = key; } /// /// Gets how the key was changed /// public CollectionChange CollectionChange { get; private set; } /// /// Gets the key that was changed /// public string Key { get; private set; } } } } ================================================ FILE: blog-samples/CSharp/TriviaBotSpeechSample/TriviaApp/packages.config ================================================  ================================================ FILE: blog-samples/CSharp/TriviaBotSpeechSample/TriviaApp/project.json ================================================ { "dependencies": { "Microsoft.Bot.Client": "0.1.0-beta", "Microsoft.NETCore.UniversalWindowsPlatform": "5.2.2", "Microsoft.Xaml.Behaviors.Uwp.Managed": "2.0.0", "Newtonsoft.Json": "9.0.1" }, "frameworks": { "uap10.0": {} }, "runtimes": { "win10-arm": {}, "win10-arm-aot": {}, "win10-x86": {}, "win10-x86-aot": {}, "win10-x64": {}, "win10-x64-aot": {} } } ================================================ FILE: blog-samples/CSharp/TriviaBotSpeechSample/TriviaBot/App_Start/WebApiConfig.cs ================================================ // Copyright (c) Microsoft Corporation. All rights reserved. using Newtonsoft.Json; using Newtonsoft.Json.Serialization; using System.Web.Http; namespace TriviaBot { public static class WebApiConfig { public static void Register(HttpConfiguration config) { // Json settings config.Formatters.JsonFormatter.SerializerSettings.NullValueHandling = NullValueHandling.Ignore; config.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); config.Formatters.JsonFormatter.SerializerSettings.Formatting = Formatting.Indented; JsonConvert.DefaultSettings = () => new JsonSerializerSettings() { ContractResolver = new CamelCasePropertyNamesContractResolver(), Formatting = Newtonsoft.Json.Formatting.Indented, NullValueHandling = NullValueHandling.Ignore, }; // Web API configuration and services // Web API routes config.MapHttpAttributeRoutes(); config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{id}", defaults: new { id = RouteParameter.Optional } ); } } } ================================================ FILE: blog-samples/CSharp/TriviaBotSpeechSample/TriviaBot/Controllers/MessagesController.cs ================================================ // Copyright (c) Microsoft Corporation. All rights reserved. using System; using System.Net; using System.Net.Http; using System.Threading.Tasks; using System.Web.Http; using Microsoft.Bot.Connector; using Microsoft.Bot.Builder.Dialogs; namespace TriviaBot { [BotAuthentication] public class MessagesController : ApiController { /// /// POST: api/Messages /// Receive a message from a user and reply to it /// public async Task Post([FromBody]Activity activity) { try { await HandleActivity(activity); } catch(Exception ex) { System.Diagnostics.Trace.WriteLine(ex.ToString()); } return Request.CreateResponse(HttpStatusCode.Accepted); } private async Task HandleActivity(Activity activity) { if (activity.Type == ActivityTypes.Message) { await HandleMessageActivity(activity); return true; } else if (activity.Type == ActivityTypes.DeleteUserData) { // Implement user deletion here // If we handle user deletion, return a real message } else if (activity.Type == ActivityTypes.ConversationUpdate) { // Handle conversation state changes, like members being added and removed // Use Activity.MembersAdded and Activity.MembersRemoved and Activity.Action for info // Not available in all channels } else if (activity.Type == ActivityTypes.ContactRelationUpdate) { // Handle add/remove from contact lists // Activity.From + Activity.Action represent what happened } else if (activity.Type == ActivityTypes.Typing) { // Handle knowing tha the user is typing } else if (activity.Type == ActivityTypes.Ping) { } return false; } private async Task HandleMessageActivity(Activity message) { /*ConnectorClient connector = new ConnectorClient(new Uri(message.ServiceUrl)); // calculate something for us to return int length = (message.Text ?? string.Empty).Length; // return our reply to the user Activity reply = message.CreateReply($"You sent {message.Text} which was {length} characters"); await connector.Conversations.ReplyToActivityAsync(reply); return false;*/ await Conversation.SendAsync(message, () => new Runtime.TriviaDialog()); } } } ================================================ FILE: blog-samples/CSharp/TriviaBotSpeechSample/TriviaBot/Global.asax ================================================ <%@ Application Codebehind="Global.asax.cs" Inherits="TriviaBot.WebApiApplication" Language="C#" %> ================================================ FILE: blog-samples/CSharp/TriviaBotSpeechSample/TriviaBot/Global.asax.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Http; using System.Web.Routing; namespace TriviaBot { public class WebApiApplication : System.Web.HttpApplication { protected void Application_Start() { GlobalConfiguration.Configure(WebApiConfig.Register); } } } ================================================ FILE: blog-samples/CSharp/TriviaBotSpeechSample/TriviaBot/Luis/LuisEntity.cs ================================================ // Copyright (c) Microsoft Corporation. All rights reserved. using System; using System.Collections.Generic; using System.Runtime.Serialization; namespace Luis { /// /// Describes an entity that was identified in the query /// [DataContract] [Serializable] public class LuisEntity : IComparable, IComparable { /// /// Initializes a new instance of the class. /// /// The entity value /// The type of the entity /// The start index of the entity value in the text /// The end index of the entity value in the text /// Confidence that the entity was properly identified public LuisEntity(string entity, string type, int startIndex, int endIndex, double score) { Entity = entity ?? string.Empty; Type = type ?? string.Empty; StartIndex = startIndex; EndIndex = endIndex; Score = score; } /// /// Gets the entity value /// [DataMember(Name = "entity")] public string Entity { get; private set; } /// /// Gets the type, or name, of the entity /// [DataMember(Name = "type")] public string Type { get; private set; } /// /// Gets the index in the query this entity starts at /// [DataMember(Name = "startIndex")] public int StartIndex { get; private set; } /// /// Gets the index in the query this entity ends at /// [DataMember(Name = "endIndex")] public int EndIndex { get; private set; } /// /// Gets the certainty this entity was identified with /// [DataMember(Name = "score")] public double Score { get; private set; } /// /// Gets the type, or name, of the entity /// [DataMember(Name = "resolution")] public Dictionary Resolution { get; private set; } /// /// Determines if left is equal to right /// /// The object to compare /// The object to compare against /// The result of the comparison public static bool operator ==(LuisEntity left, LuisEntity right) { if (object.ReferenceEquals(left, null)) { return object.ReferenceEquals(right, null); } return left.Equals(right); } /// /// Determines if left is not equal to right /// /// The object to compare /// The object to compare against /// The result of the comparison public static bool operator !=(LuisEntity left, LuisEntity right) { return !(left == right); } /// /// Determines if left is less than right /// /// The object to compare /// The object to compare against /// The result of the comparison public static bool operator <(LuisEntity left, LuisEntity right) { return Compare(left, right) < 0; } /// /// Determines if left is greater than right /// /// The object to compare /// The object to compare against /// The result of the comparison public static bool operator >(LuisEntity left, LuisEntity right) { return Compare(left, right) > 0; } /// /// Compare left with right /// /// The object to compare /// The object to compare against /// The result of the comparison public static int Compare(LuisEntity left, LuisEntity right) { if (object.ReferenceEquals(left, right)) { return 0; } if (object.ReferenceEquals(left, null)) { return -1; } return left.CompareTo(right); } /// /// Gets the string representation of this object /// /// The string representation of this object public override string ToString() { return "{ Entity = " + Entity + ", Type = " + Type + ", StartIndex = " + StartIndex + ", EndIndex = " + EndIndex + ", Score = " + Score + " }"; } /// /// Compares this EntityDefinition to the passed in object /// /// The object to compare this EntityDefinition with /// The result of the comparison public int CompareTo(object obj) { if (obj == null) { return 1; } LuisEntity other = obj as LuisEntity; if (other == null) { throw new ArgumentException("A EntityDefinition object is required for comparison.", "obj"); } return CompareTo(other); } /// /// Compares this EntityDefinition to the passed in EntityDefinition /// The ordering follows the following precedence (stopping when one value differs) /// Higher score /// Case insensitive lexicographic ordering Type /// Case sensitive lexicographic Entity (value) /// Earlier in the string first /// Shorter first /// /// The EntityDefinition to compare against /// The result of the comparison public int CompareTo(LuisEntity other) { if (object.ReferenceEquals(other, null)) { return 1; } // Compare score (higher comes first) var comparison = other.Score.CompareTo(Score); // Compare Type if (comparison == 0) { comparison = string.Compare(Type, other.Type, StringComparison.Ordinal); } // Compare Entity if (comparison == 0) { comparison = string.Compare(Entity, other.Entity, StringComparison.Ordinal); } // Compare StartIndex if (comparison == 0) { comparison = StartIndex.CompareTo(other.StartIndex); } // Compare EndIndex if (comparison == 0) { comparison = EndIndex.CompareTo(other.EndIndex); } return comparison; } /// /// Determines if this EntityDefinition is equal to another /// /// The object to compare this EntityDefinition with /// Whether the objects are equal public override bool Equals(object obj) { var other = obj as LuisEntity; if (object.ReferenceEquals(other, null)) { return false; } return CompareTo(other) == 0; } /// /// A hash code for this object that will always be the same for EntityDefinition objects with the /// same values /// /// The generated hash code public override int GetHashCode() { int hashCode = Entity.GetHashCode(); hashCode = (hashCode * 251) + Type.GetHashCode(); hashCode = (hashCode * 251) + StartIndex.GetHashCode(); hashCode = (hashCode * 251) + EndIndex.GetHashCode(); hashCode = (hashCode * 251) + Score.GetHashCode(); return hashCode; } } } ================================================ FILE: blog-samples/CSharp/TriviaBotSpeechSample/TriviaBot/Luis/LuisIntent.cs ================================================ // Copyright (c) Microsoft Corporation. All rights reserved. using System; using System.Runtime.Serialization; namespace Luis { /// /// Describes an intent /// [DataContract] public class LuisIntent : IComparable, IComparable { /// /// Gets or sets the intent name /// [DataMember(Name = "intent")] public string Intent { get; set; } /// /// Gets or sets the certainty for this intent /// [DataMember(Name = "score")] public double Score { get; set; } /// /// Determines if left is equal to right /// /// The object to compare /// The object to compare against /// The result of the comparison public static bool operator ==(LuisIntent left, LuisIntent right) { if (object.ReferenceEquals(left, null)) { return object.ReferenceEquals(right, null); } return left.Equals(right); } /// /// Determines if left is not equal to right /// /// The object to compare /// The object to compare against /// The result of the comparison public static bool operator !=(LuisIntent left, LuisIntent right) { return !(left == right); } /// /// Determines if left is less than right /// /// The object to compare /// The object to compare against /// The result of the comparison public static bool operator <(LuisIntent left, LuisIntent right) { return Compare(left, right) < 0; } /// /// Determines if left is greater than right /// /// The object to compare /// The object to compare against /// The result of the comparison public static bool operator >(LuisIntent left, LuisIntent right) { return Compare(left, right) > 0; } /// /// Compare left with right /// /// The object to compare /// The object to compare against /// The result of the comparison public static int Compare(LuisIntent left, LuisIntent right) { if (object.ReferenceEquals(left, right)) { return 0; } if (object.ReferenceEquals(left, null)) { return -1; } return left.CompareTo(right); } /// /// Gets the string representation of this object /// /// The string representation of this object public override string ToString() { return "{ Intent = " + Intent.ToString() + ", Score = " + Score.ToString(System.Globalization.CultureInfo.InvariantCulture) + " }"; } /// /// Compares this IntentDefinition to the passed in object /// /// The object to compare this IntentDefinition with /// The result of the comparison public int CompareTo(object obj) { if (obj == null) { return 1; } LuisIntent other = obj as LuisIntent; if (other == null) { throw new ArgumentException("A IntentDefinition object is required for comparison.", "obj"); } return CompareTo(other); } /// /// Compares this IntentDefinition to the passed in IntentDefinition /// /// The IntentDefinition to compare against /// The result of the comparison public int CompareTo(LuisIntent other) { if (object.ReferenceEquals(other, null)) { return 1; } var comparison = Score.CompareTo(other.Score); if (comparison == 0) { comparison = string.Compare(Intent, other.Intent, StringComparison.OrdinalIgnoreCase); } return comparison; } /// /// Determines if this IntentDefinition is equal to another /// /// The object to compare this IntentDefinition with /// Whether the objects are equal public override bool Equals(object obj) { var other = obj as LuisIntent; if (object.ReferenceEquals(other, null)) { return false; } return CompareTo(other) == 0; } /// /// A hash code for this object that will always be the same for IntentDefinition objects with the /// same values /// /// The generated hash code public override int GetHashCode() { int hashCode = Intent.GetHashCode(); hashCode = (hashCode * 251) + Score.GetHashCode(); return hashCode; } } } ================================================ FILE: blog-samples/CSharp/TriviaBotSpeechSample/TriviaBot/Luis/LuisResult.cs ================================================ // Copyright (c) Microsoft Corporation. All rights reserved. using System.Collections.Generic; using System.Runtime.Serialization; namespace Luis { /// /// The data structure of the JSON returned by LUIS used for deserialization /// [DataContract] public class LuisResult { /// /// Gets or sets the LUIS query that this object was processed from /// [DataMember(Name = "query")] public string Query { get; set; } /// /// Gets the list of intents that the query might relate to /// [DataMember(Name = "intents")] public ICollection Intents { get; private set; } /// /// Gets the entities that were identified from the query /// [DataMember(Name = "entities")] public ICollection Entities { get; private set; } } } ================================================ FILE: blog-samples/CSharp/TriviaBotSpeechSample/TriviaBot/Luis/QueryLuis.cs ================================================ // Copyright (c) Microsoft Corporation. All rights reserved. using System; using System.Collections.Generic; using System.IO; using System.Net; using System.Runtime.Serialization.Json; using System.Threading.Tasks; using static TriviaBot.Runtime.Utility; namespace Luis { /// /// Contains the default logic for converting text, or the result from the speech recognizer, /// into entities that will be finalized in the conversation /// public static class QueryLuis { /// /// Extract the best matching intent and all entities from utterance by calling LUIS with the specified LUISInfo connection data. /// /// Connection data for the LUIS recognizer. /// Text to check for intents (typed or from the speech recognizer). /// LuisResult with the intent and entities recognized by LUIS. Entities won't be returned if intent didn't match. public static async Task GetIntentAndEntitiesFromLuis( string appid, string key, string utterance) { ArgumentNotNull(appid, "appid"); ArgumentNotNull(key, "key"); ArgumentNotNull(utterance, "utterance"); // Query LUIS and generate our response var luisResult = await QueryLuisWithRetry(appid, key, utterance, 3); // LUIS can return more than one of the same entity // with different values and scores, we sort by the score // but don't retain the score otherwise. return luisResult; } /// /// Attempts to query a LUIS application, ignoring exceptions up until the last attempt /// /// All information needed to query the LUIS application /// The text query to send to LUIS /// How many times to retry on failure /// The deserialized data returned from LUIS private static async Task QueryLuisWithRetry( string appid, string key, string query, int retries) { LuisResult result = null; Exception exception = null; int tooManyRequestsDelay = 125; for (var attempt = 0; attempt < retries; ++attempt) { exception = null; try { result = await CallLuis(appid, key, query); break; } catch (Exception e) { // Check for 429 "Too many requests" error. exception = e; if (e.IfIs( we => we.Response.IfIs( wr => (int)wr.StatusCode == 429, false), false)) { await Task.Delay(tooManyRequestsDelay); tooManyRequestsDelay *= 3; } else { throw; } } } return result; } /// /// Queries the specified LUIS application with the provided query /// /// The information needed to query the LUIS application /// The query to supply the LUIS application /// The deserialized output of the LUIS application private static async Task CallLuis( string appid, string key, string query) { Stream responseStream = null; var requestUrl = "https://westus.api.cognitive.microsoft.com/luis/v2.0/apps/" + appid + "?subscription-key=" + key + "&timezoneOffset=0&verbose=true&q=" + query; requestUrl = Uri.EscapeUriString(requestUrl); HttpWebRequest request = WebRequest.Create(requestUrl) as HttpWebRequest; var response = await request.GetResponseAsync() as HttpWebResponse; if (response.StatusCode != HttpStatusCode.OK) { throw new Exception($"Error communicating with LUIS application {appid}:\nServer error (HTTP {response.StatusCode}: {response.StatusDescription})."); } responseStream = response.GetResponseStream(); DataContractJsonSerializer jsonSerializer = new DataContractJsonSerializer(typeof(LuisResult)); var objResponse = jsonSerializer.ReadObject(responseStream); var jsonResponse = objResponse as LuisResult; return jsonResponse; } } } ================================================ FILE: blog-samples/CSharp/TriviaBotSpeechSample/TriviaBot/Properties/AssemblyInfo.cs ================================================ using System.Reflection; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. [assembly: AssemblyTitle("TriviaBot")] [assembly: AssemblyDescription("A speech-enabled triva bot built on top of the Bot Framework C# SDK that uses http://opentdb.com/")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("Microsoft")] [assembly: AssemblyProduct("TriviaBot")] [assembly: AssemblyCopyright("Copyright © 2017")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] // Setting ComVisible to false makes the types in this assembly not visible // to COM components. If you need to access a type in this assembly from // COM, set the ComVisible attribute to true on that type. [assembly: ComVisible(false)] // The following GUID is for the ID of the typelib if this project is exposed to COM [assembly: Guid("a8ba1066-5695-4d71-abb4-65e5a5e0c3d4")] // Version information for an assembly consists of the following four values: // // Major Version // Minor Version // Build Number // Revision // // You can specify all the values or you can default the Revision and Build Numbers // by using the '*' as shown below: [assembly: AssemblyVersion("1.0.0.0")] [assembly: AssemblyFileVersion("1.0.0.0")] ================================================ FILE: blog-samples/CSharp/TriviaBotSpeechSample/TriviaBot/Runtime/BotState.cs ================================================ // Copyright (c) Microsoft Corporation. All rights reserved. namespace TriviaBot.Runtime { public enum BotState { None, Trivia, SwitchCategory, WaitingForReengagement } } ================================================ FILE: blog-samples/CSharp/TriviaBotSpeechSample/TriviaBot/Runtime/Categories.cs ================================================ // Copyright (c) Microsoft Corporation. All rights reserved. using System.ComponentModel.DataAnnotations; namespace TriviaBot.Runtime { /// /// This list of categories comes from the list of supported categories on https://opentdb.com/ /// public enum TriviaCategory { None = -1, [Display(Name = "Any")] Any = 0, [Display(Name = "General Knowledge")] GeneralKnowledge = 9, [Display(Name = "Entertainment: Books")] EntertainmentBooks, [Display(Name = "Entertainment: Film")] EntertainmentFilm, [Display(Name = "Entertainment: Music")] EntertainmentMusic, [Display(Name = "Entertainment: Musicals & Theatres")] EntertainmentMusicalsTheatre, [Display(Name = "Entertainment: Television")] EntertainmentTelevision, [Display(Name = "Entertainment: Video Games")] EntertainmentVideoGames, [Display(Name = "Entertainment: Board Games")] EntertainmentBoardGames, [Display(Name = "Science & Nature")] ScienceNature, [Display(Name = "Science: Computers")] ScienceComputers, [Display(Name = "Science: Mathematics")] ScienceMathematics, [Display(Name = "Mythology")] Mythology, [Display(Name = "Sports")] Sports, [Display(Name = "Geography")] Geography, [Display(Name = "History")] History, [Display(Name = "Politics")] Politics, [Display(Name = "Art")] Art, [Display(Name = "Celebrities")] Celebrities, [Display(Name = "Animals")] Animals, [Display(Name = "Vehicles")] Vehicles, [Display(Name = "Entertainment: Comics")] EntertainmentComics, [Display(Name = "Science: Gadgets")] ScienceGadgets, [Display(Name = "Entertainment: Japanese Anime & Manga")] EntertainmentJapaneseAnimeManga, } } ================================================ FILE: blog-samples/CSharp/TriviaBotSpeechSample/TriviaBot/Runtime/EnumExtensions.cs ================================================ // Copyright (c) Microsoft Corporation. All rights reserved. using System; using System.ComponentModel.DataAnnotations; using System.Reflection; namespace TriviaBot.Runtime { public static class EnumExtensions { public static string DisplayName(this Enum value) { var members = value?.GetType()?.GetMember(value.ToString()); if (members != null) { foreach (var member in members) { var displayName = member.GetCustomAttribute()?.GetName(); if (displayName?.Length > 0) { return displayName; } } } return value?.ToString(); } } } ================================================ FILE: blog-samples/CSharp/TriviaBotSpeechSample/TriviaBot/Runtime/Extensions.cs ================================================ // Copyright (c) Microsoft Corporation. All rights reserved. using System.Collections.Generic; using System.Linq; namespace TriviaBot { public static class Extensions { public static string Normalize(this string msg) { return msg.ToLower().Trim().TrimEnd('.'); } public static bool NormalizedEquals(this string msg, string other) { return Normalize(msg) == Normalize(other); } public static bool ContainsIgnoreCase(this string msg, IEnumerable parts) { var msgL = msg?.ToLower(); return parts.Any(x => msgL?.Contains(x.ToLower()) == true); } } } ================================================ FILE: blog-samples/CSharp/TriviaBotSpeechSample/TriviaBot/Runtime/Responses.cs ================================================ // Copyright (c) Microsoft Corporation. All rights reserved. using Microsoft.Bot.Builder.Dialogs; using Microsoft.Bot.Connector; using Newtonsoft.Json.Linq; using System; using System.Collections.Generic; using System.IO; using System.Text; using System.Threading.Tasks; using System.Xml; using TriviaBot.Shared; namespace TriviaBot.Runtime { /// /// Contains all of the reponses from the bot to the user. /// This separates content from the core bot logic, and allows reuse. /// public static class Responses { public async static Task Send_LetsGo(IDialogContext context, IMessageActivity message) { var reply = CreateResponse( context, message, "Let's go!", "Let's go!", messageType: MessageType.Statement, inputHint: InputHints.IgnoringInput); await context.PostAsync(reply); } public async static Task Send_Goodbye(IDialogContext context, IMessageActivity message) { var reply = CreateResponse( context, message, "Goodbye.", "See you next time!", messageType: MessageType.Statement); await context.PostAsync(reply); } public async static Task Send_TimeForNextQuestion(IDialogContext context, IMessageActivity message) { var replyText = SelectRandomString(new string[] { "Alright, here's the next one", "Here's one more", $"Ok, next question", "Time for the next one" }); var reply = CreateResponse( context, message, displayText: replyText + ":", speakText: replyText, messageType: MessageType.Statement, inputHint: InputHints.IgnoringInput); await context.PostAsync(reply); } public async static Task Send_Greeting(IDialogContext context, IMessageActivity message) { var reply = CreateResponse( context, message, "Welcome to Trivia!", "Welcome to Trivia! To stop playing you can say. Cancel, or, I'm done playing, at any time. Now, Let's get started.", audioToPlay: "a_sunshine_intro_09.wav", messageType: MessageType.Statement, inputHint: InputHints.IgnoringInput); if(message.ChannelId?.ToLower() != "cortana") { reply.Attachments.Add((new ThumbnailCard() { Images = new[] { new CardImage("https://trivasdkbot.azurewebsites.net/Assets/trivia.png") }, }).ToAttachment()); } await context.PostAsync(reply); } public async static Task Send_FirstQuestion(IDialogContext context, IMessageActivity message) { var reply = CreateResponse( context, message, "Here's your first question:", "Here's your first question.", messageType: MessageType.Statement, inputHint: InputHints.IgnoringInput); await context.PostAsync(reply); } public async static Task SendQuestion(IDialogContext context, IMessageActivity message, string question, IList options) { // Send Question & possible answers var reply = CreateResponse( context, message, question, question, messageType: MessageType.Question, optionsToAdd: options, inputHint: InputHints.ExpectingInput); await context.PostAsync(reply); } public async static Task Send_DidNotUnderstand(IDialogContext context, IMessageActivity message) { var reply = CreateResponse( context, message, "Sorry, I don't understand that.", "Sorry, I don't understand that.", messageType: MessageType.Statement, inputHint: InputHints.IgnoringInput); await context.PostAsync(reply); } public async static Task Send_WhichOne(IDialogContext context, IMessageActivity message, IList options) { var replyText = "Which one did you mean?"; var reply = CreateResponse( context, message, replyText, replyText, messageType: MessageType.Question, optionsToAdd: options, inputHint: InputHints.ExpectingInput); System.Diagnostics.Trace.WriteLine("message: " + Newtonsoft.Json.JsonConvert.SerializeObject(message)); System.Diagnostics.Trace.WriteLine("reply: " + Newtonsoft.Json.JsonConvert.SerializeObject(reply)); await context.PostAsync(reply); } public async static Task AskForCategory(IDialogContext context, IMessageActivity message) { var replyText = "What category would you like to switch to?"; var reply = CreateResponse( context, message, replyText, replyText, messageType: MessageType.Statement, inputHint: InputHints.ExpectingInput); await context.PostAsync(reply); } public async static Task Send_ConfirmAnswer(IDialogContext context, IMessageActivity message, string choice) { var confirmationReply = CreateResponse( context, message, $"Is {choice} your final answer?", $"I think you said {choice}. Is that your final answer?", messageType: MessageType.Statement, inputHint: InputHints.ExpectingInput); await context.PostAsync(confirmationReply); } public async static Task Send_CorrectAnswer(IDialogContext context, IMessageActivity message, string correctAnswer) { var replyText = SelectRandomString(new string[] { "Congrats, you got it right!", "Correct!", $"That's right, the answer is {correctAnswer}", "100% correct!" }); var reply = CreateResponse( context, message, replyText, replyText, messageType: MessageType.GotItRight, audioToPlay: "tv_gameshow_bell_01.wav", inputHint: InputHints.IgnoringInput); await context.PostAsync(reply); } public async static Task Send_OutOfTime_First(IDialogContext context, IMessageActivity message) { var replyText = SelectRandomString(new string[] { "Oops! You ran out of time!", "Come on, you've gotta be faster than that!", "You don't have to be fast, but you can't be that slow!", "I don't have all day!" }); var reply = CreateResponse( context, message, replyText, replyText, messageType: MessageType.OutOfTime, inputHint: InputHints.IgnoringInput); await context.PostAsync(reply); } public async static Task Send_OutOfTime_X(IDialogContext context, IMessageActivity message) { var replyText = SelectRandomString(new string[] { "Did you forget you were playing? Say something to continue.", "Fine, I won't talk to you either! Say something to continue.", "You don't have to be fast, but you can't be that slow! Say something to continue.", "Again? Say something to continue." }); var reply = CreateResponse( context, message, replyText, replyText, messageType: MessageType.OutOfTime, inputHint: InputHints.AcceptingInput); await context.PostAsync(reply); } public async static Task Send_TryEasierQuestion(IDialogContext context, IMessageActivity message) { var replyText = SelectRandomString(new string[] { "Let's try something a little easier this time...", "Maybe this one will be better.", "I believe in you, let's keep going!", "Let's try a different question this time." }); var reply = CreateResponse( context, message, replyText, replyText, messageType: MessageType.Statement, inputHint: InputHints.IgnoringInput); await context.PostAsync(reply); } public async static Task Send_IncorrectAnswer(IDialogContext context, IMessageActivity message, string correctAnswer) { var replyText = SelectRandomString(new string[] { $"Sorry, the correct answer was: {correctAnswer}", $"Not quite, the correct answer was: {correctAnswer}", $"That's incorrect. The answer was: {correctAnswer}" }); var reply = CreateResponse( context, message, replyText, replyText, messageType: MessageType.GotItWrong, audioToPlay: "tv_gameshow_buzzer_03.wav", inputHint: InputHints.IgnoringInput); await context.PostAsync(reply); } public async static Task Send_Help_Game(IDialogContext context, IMessageActivity message) { var reply = CreateResponse( context, message, "Here are some things you can say to me: \"I'd like to play a game\", \"Let's play trivia\"", "Here are some things you can say to me: \"I'd like to play a game\", \"Let's play trivia\"", messageType: MessageType.Statement, inputHint: InputHints.AcceptingInput); await context.PostAsync(reply); } public async static Task Send_Help_Trivia(IDialogContext context, IMessageActivity message) { var reply = CreateResponse( context, message, "Here are some things you can say to me: \"I'd like to play trivia\", \"How about trivia\"", "Here are some things you can say to me: \"I'd like to play trivia\", \"How about trivia\"", messageType: MessageType.Statement, inputHint: InputHints.AcceptingInput); await context.PostAsync(reply); } public async static Task Send_Help_Trivia(IDialogContext context, IMessageActivity message, IList options) { var replyText = "You're playing trivia! Here are your choices:"; var reply = CreateResponse( context, message, replyText, replyText, messageType: MessageType.Statement, optionsToAdd: options, inputHint: InputHints.ExpectingInput); await context.PostAsync(reply); } public async static Task Send_Error_UnknownState(IDialogContext context, IMessageActivity message) { var reply = CreateResponse( context, message, "Sorry, I seem to have gotten into a bad state. I'll try resetting the conversation.", "Sorry, I seem to have gotten into a bad state. I'll try resetting the conversation.", messageType: MessageType.Statement, inputHint: InputHints.AcceptingInput); await context.PostAsync(reply); } public async static Task Send_Error_NextQuestion(IDialogContext context, IMessageActivity message) { var errorReply = CreateResponse( context, message, "Sorry, an error occurred. Here's another question.", "I seem to have hit a snag, let's try another question.", messageType: MessageType.Statement, inputHint: InputHints.IgnoringInput); await context.PostAsync(errorReply); } public async static Task Send_PlayingCategory(IDialogContext context, IMessageActivity message, TriviaCategory category) { var replyText = SelectRandomString(new string[] { $"We're playing: \"{category.DisplayName()}\".", $"We're going with: \"{category.DisplayName()}\" today.", $"You'll get questions about: \"{category.DisplayName()}\"." }); var reply = CreateResponse( context, message, replyText, replyText, messageType: MessageType.Statement, inputHint: InputHints.IgnoringInput); await context.PostAsync(reply); } public async static Task Send_SwitchedCategory(IDialogContext context, IMessageActivity message, TriviaCategory category) { var replyText = $"You've switched to the category \"{category.DisplayName()}\""; var reply = CreateResponse( context, message, replyText, replyText, messageType: MessageType.Statement, inputHint: InputHints.IgnoringInput); await context.PostAsync(reply); } public async static Task Send_LightningModeStart(IDialogContext context, IMessageActivity message) { if (message.ChannelId.NormalizedEquals("directline")) { var replyText = "Starting lightning mode!"; var reply = CreateResponse( context, message, replyText, replyText, messageType: MessageType.StartLightningMode, inputHint: InputHints.IgnoringInput); await context.PostAsync(reply); } else if (message.ChannelId.NormalizedEquals("cortana")) { var replyText = "You can play lightning mode in our app! I'll bring you there..."; var reply = CreateResponse( context, message, replyText, replyText, messageType: MessageType.Statement, inputHint: InputHints.IgnoringInput); await context.PostAsync(reply); reply = CreateResponse( context, message, null, null, messageType: MessageType.StartLightningMode, inputHint: InputHints.IgnoringInput); await context.PostAsync(reply); reply.ChannelData = JObject.FromObject(new { action = new { type = "LaunchUri", uri = "triviaapp://play/gameshow" } }); await context.PostAsync(reply); } else { var replyText = "Sorry, Lightning Mode is only supported in our app."; var reply = CreateResponse( context, message, replyText, replyText, messageType: MessageType.Statement, inputHint: InputHints.IgnoringInput); await context.PostAsync(reply); } } public async static Task Send_LightningModeEnd(IDialogContext context, IMessageActivity message) { if (message.ChannelId.NormalizedEquals("directline")) { var replyText = "Turning off lightning mode."; var reply = CreateResponse( context, message, replyText, replyText, messageType: MessageType.StopLightningMode, inputHint: InputHints.IgnoringInput); await context.PostAsync(reply); } else { var replyText = "Sorry, Lightning Mode is only supported in our app. "; var reply = CreateResponse( context, message, replyText, replyText, messageType: MessageType.Statement, inputHint: InputHints.IgnoringInput); await context.PostAsync(reply); } } private static IMessageActivity CreateResponse ( IDialogContext context, IMessageActivity message, string displayText, string speakText, MessageType messageType, string audioToPlay = null, IList optionsToAdd = null, string inputHint = InputHints.AcceptingInput ) { var activityToSend = context.MakeMessage(); if (displayText != null) { activityToSend.Text = displayText; } var ssml = (string)null; if (speakText != null) { var escapedSpeakText = speakText.Replace("&", "&").Replace("<", "<").Replace(">", ">").Replace("\"", """).Replace("'", "'"); ssml = SsmlWrapper.Wrap(escapedSpeakText); if (audioToPlay != null && message.ChannelId != "cortana") { var assetPath = "http://" + System.Web.HttpContext.Current.Request.ServerVariables["SERVER_NAME"] + "/Assets/"; var uri = new Uri(assetPath + audioToPlay); ssml = CombineAudioAndTextForSSML(uri, ssml); } } activityToSend.Speak = ssml; activityToSend.InputHint = inputHint; var appEntities = new AppEntities { MessageType = messageType, TriviaAnswerOptions = optionsToAdd?.Count > 0 ? optionsToAdd : null }; if (optionsToAdd != null) { List cardButtons = new List(); bool numberOptions = optionsToAdd.Count > 2; for (int i = 0; i < optionsToAdd.Count; i++) { var display = numberOptions ? $"{(i + 1).ToString()}: {optionsToAdd[i]}" : optionsToAdd[i]; cardButtons.Add(new CardAction() { Value = optionsToAdd[i], Type = "postBack", Title = display }); } var plCard = new ThumbnailCard() { // Title = "Pick an answer", Buttons = cardButtons, }; activityToSend.Attachments.Add(plCard.ToAttachment()); } var entity = new Entity(); entity.SetAs(appEntities); activityToSend.Entities.Add(entity); return activityToSend; } public static string GetInnerSsmlContents(string ssml) { StringBuilder sb = new StringBuilder(); XmlReader reader = null; reader = XmlReader.Create(new StringReader(ssml)); string inner = ""; if (reader.Read()) { inner = reader.ReadInnerXml(); } return inner; } public static string CombineAudioAndTextForSSML(Uri audioStream, string text) { StringBuilder sb = new StringBuilder(); const string ssmlPrefix = @""; const string ssmlSuffix = ""; sb.Append(ssmlPrefix); sb.Append($"