/// <summary> /// This method gets called by the runtime. Use this method to add services to the container. /// </summary> /// <param name="services">The <see cref="IServiceCollection"/> specifies the contract for a collection of service descriptors.</param> /// <seealso cref="IStatePropertyAccessor{T}"/> /// <seealso cref="https://docs.microsoft.com/en-us/aspnet/web-api/overview/advanced/dependency-injection"/> /// <seealso cref="https://docs.microsoft.com/en-us/azure/bot-service/bot-service-manage-channels?view=azure-bot-service-4.0"/> public void ConfigureServices(IServiceCollection services) { services.Configure <MySettings>(Configuration); services.AddMvc(); //#if DEBUG // services.AddMvc(); //#else // services.AddMvc(mvcConfig => // { // mvcConfig.Filters.Add(new BasicAuthFilter(Configuration["BasicAuthUsername"], Configuration["BasicAuthPassword"])); // }); //#endif // create luis recognizer var luisApplication = new LuisApplication( Configuration["LuisAppId"], Configuration["LuisAPIKey"], "https://" + Configuration["LuisAPIHostName"]); services.AddSingleton(new LuisRecognizer(luisApplication)); // Create the credential provider to be used with the Bot Framework Adapter. services.AddSingleton <ICredentialProvider, ConfigurationCredentialProvider>(); // Create the Bot Framework Adapter with error handling enabled. services.AddSingleton <IBotFrameworkHttpAdapter, AdapterWithErrorHandler>(); // Create the storage we'll be using for User and Conversation state. (Memory is great for testing purposes.) services.AddSingleton <IStorage, MemoryStorage>(); // Create the User state. (Used in this bot's Dialog implementation.) services.AddSingleton <UserState>(); // Create the Conversation state. (Used by the Dialog system itself.) services.AddSingleton <ConversationState>(); // The Dialog that will be run by the bot. services.AddSingleton <ReservationDialog>(); // Memory Storage is for local bot debugging only. When the bot // is restarted, everything stored in memory will be gone. IStorage dataStore = new MemoryStorage(); // Create and add conversation state. var conversationState = new ConversationState(dataStore); services.AddSingleton(conversationState); var userState = new UserState(dataStore); services.AddSingleton(userState); // Add the personality chat middleware var personalityChatOptions = new PersonalityChatMiddlewareOptions( respondOnlyIfChat: true, scoreThreshold: 0.5F, botPersona: PersonalityChatPersona.Humorous); services.AddSingleton(new PersonalityChatMiddleware(personalityChatOptions)); // Add the translator speech middleware services.AddTransient <IBot, EchoBot>(); // Create and register state accessors. // Accessors created here are passed into the IBot-derived class on every turn. services.AddSingleton(sp => { // We need to grab the conversationState we added on the options in the previous step var options = sp.GetRequiredService <IOptions <BotFrameworkOptions> >().Value; if (options == null) { throw new InvalidOperationException("BotFrameworkOptions must be configured prior to setting up the State Accessors"); } // Create the custom state accessor. // State accessors enable other components to read and write individual properties of state. var accessors = new EchoBotAccessors(conversationState, userState) { // Initialize Dialog State ConversationDialogState = conversationState.CreateProperty <DialogState>("DialogState"), ReservationState = userState.CreateProperty <ReservationData>("ReservationState"), }; return(accessors); }); // Add QnA Maker here // We add the the QnA service to the connected services. // Create and register a QnA service and knowledgebase services.AddSingleton(sp => { return(new QnAMaker( new QnAMakerEndpoint { EndpointKey = Configuration["QnAEndpointKey"], Host = Configuration["QnAHostname"], KnowledgeBaseId = Configuration["QnAKbId"], }, new QnAMakerOptions { ScoreThreshold = 0.9f, Top = 1, })); }); }
protected override async Task OnMessageActivityAsync(ITurnContext <IMessageActivity> turnContext, CancellationToken cancellationToken) { turnContext.Activity.RemoveRecipientMention(); var commandName = turnContext.Activity.Text?.Trim(); if (string.IsNullOrEmpty(commandName)) { var value = turnContext.Activity.Value; return; } AdaptiveCard card = null; switch (commandName) { case "marks": { var result = AdaptiveCard.FromJson(File.ReadAllText(@".\Cards\json\MarksForm.json")); card = result.Card; } break; case "img-base64": { var result = AdaptiveCard.FromJson(File.ReadAllText(@".\Cards\json\ImageCard.json")); card = result.Card; } break; case "img": { var result = AdaptiveCard.FromJson(File.ReadAllText(@".\Cards\json\ImageCardUrl.json")); card = result.Card; } break; case "prompts": { await _promptDialog.RunAsync(turnContext, _conversationState.CreateProperty <DialogState>(nameof(DialogState)), cancellationToken); return; } case "carousel": { Activity replyToConversation = MessageFactory.Text("Should see in carousel format"); replyToConversation.AttachmentLayout = AttachmentLayoutTypes.Carousel; replyToConversation.Attachments = new List <Attachment>(); var card1 = AdaptiveCard.FromJson(File.ReadAllText(@".\Cards\json\card1.json")); var card2 = AdaptiveCard.FromJson(File.ReadAllText(@".\Cards\json\card2.json")); replyToConversation.Attachments.Add(CreateApativeCardAttachment(card1.Card)); replyToConversation.Attachments.Add(CreateApativeCardAttachment(card2.Card)); await turnContext.SendActivityAsync(replyToConversation); return; } case "invoke": { var c = new HeroCard { Title = "Task Module Demo", Text = "Click the below button to launch Task Module", Buttons = new List <CardAction> { new CardAction { Type = "invoke", Title = "Open Task Module", Text = "Open Task Module", Value = "{\"type\": \"task/fetch\", \"data\": \"alertform\"}" }, new CardAction { Type = "openUrl", Title = "StageViewDeeplink", Value = "https://teams.microsoft.com/l/stage/2a527703-1f6f-4559-a332-d8a7d288cd88/0?context={\"contentUrl\":\"https%3A%2F%2Fmicrosoft.sharepoint.com%2Fteams%2FLokisSandbox%2FSitePages%2FSandbox-Page.aspx\", \"websiteURL\":\"https%3A%2F%2Fmicrosoft.sharepoint.com%2Fteams%2FLokisSandbox%2FSitePages%2FSandbox-Page.aspx\", \"title\":\"Contoso\"}", } } }; await turnContext.SendActivityAsync(MessageFactory.Attachment(c.ToAttachment())); return; } default: await turnContext.SendActivityAsync(MessageFactory.Text("Complete!"), cancellationToken); return; } await turnContext.SendActivityAsync( MessageFactory.Attachment(CreateApativeCardAttachment(card)), cancellationToken); }
/// <summary> /// This method gets called by the runtime. Use this method to add services to the container. /// </summary> /// <param name="services">The <see cref="IServiceCollection"/> specifies the contract for a collection of service descriptors.</param> /// <seealso cref="IStatePropertyAccessor{T}"/> /// <seealso cref="https://docs.microsoft.com/en-us/aspnet/web-api/overview/advanced/dependency-injection"/> /// <seealso cref="https://docs.microsoft.com/en-us/azure/bot-service/bot-service-manage-channels?view=azure-bot-service-4.0"/> public void ConfigureServices(IServiceCollection services) { // 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(); // For production bots use the Azure Blob or // Azure CosmosDB storage providers. For the Azure // based storage providers, add the Microsoft.Bot.Builder.Azure // Nuget package to your solution. That package is found at: // https://www.nuget.org/packages/Microsoft.Bot.Builder.Azure/ // Uncomment the following lines to use Azure Blob Storage // //Storage configuration name or ID from the .bot file. // const string StorageConfigurationId = "<STORAGE-NAME-OR-ID-FROM-BOT-FILE>"; // var blobConfig = botConfig.FindServiceByNameOrId(StorageConfigurationId); // if (!(blobConfig is BlobStorageService blobStorageConfig)) // { // throw new InvalidOperationException($"The .bot file does not contain an blob storage with name '{StorageConfigurationId}'."); // } // // Default container name. // const string DefaultBotContainer = "<DEFAULT-CONTAINER>"; // var storageContainer = string.IsNullOrWhiteSpace(blobStorageConfig.Container) ? DefaultBotContainer : blobStorageConfig.Container; // IStorage dataStore = new Microsoft.Bot.Builder.Azure.AzureBlobStorage(blobStorageConfig.ConnectionString, storageContainer); // Create Conversation State object. // The Conversation State object is where we persist anything at the conversation-scope. var conversationState = new ConversationState(dataStore); services.AddSingleton(conversationState); var userState = new UserState(dataStore); services.AddSingleton(userState); services.AddSingleton <StateAccessor>(sp => { // Create Custom State Property accessors // State Property Accessors enable components to read and write individual properties, without having to // pass the entire state object. var accessors = new StateAccessor { ConversationDialogState = conversationState.CreateProperty <DialogState>(StateAccessor.DialogStateName), }; return(accessors); }); services.AddBot <Bot>(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 ?? @".\BotConfiguration.bot", secretKey); services.AddSingleton(sp => botConfig ?? throw new InvalidOperationException($"The .bot config file could not be loaded. ({botConfig})")); // 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 <Bot>(); // 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 async Task OAuthPromptWithMagicCode() { var convoState = new ConversationState(new MemoryStorage()); var dialogState = convoState.CreateProperty <DialogState>("dialogState"); var adapter = new TestAdapter() .Use(new AutoSaveStateMiddleware(convoState)); // Create new DialogSet. var dialogs = new DialogSet(dialogState); dialogs.Add(new OAuthPrompt("OAuthPrompt", new OAuthPromptSettings() { Text = "Please sign in", ConnectionName = ConnectionName, Title = "Sign in" })); BotCallbackHandler botCallbackHandler = async(turnContext, cancellationToken) => { var dc = await dialogs.CreateContextAsync(turnContext, cancellationToken); var results = await dc.ContinueDialogAsync(cancellationToken); if (results.Status == DialogTurnStatus.Empty) { var options = new PromptOptions { Prompt = new Activity { Type = ActivityTypes.Message, Text = "Please select an option." }, RetryPrompt = new Activity { Type = ActivityTypes.Message, Text = "Retrying - Please select an option." } }; await dc.PromptAsync("OAuthPrompt", options, cancellationToken); } else if (results.Status == DialogTurnStatus.Complete) { if (results.Result is TokenResponse) { await turnContext.SendActivityAsync(MessageFactory.Text("Logged in."), cancellationToken); } else { await turnContext.SendActivityAsync(MessageFactory.Text("Failed."), cancellationToken); } } }; await new TestFlow(adapter, botCallbackHandler) .Send("hello") .AssertReply(activity => { Assert.Single(((Activity)activity).Attachments); Assert.Equal(OAuthCard.ContentType, ((Activity)activity).Attachments[0].ContentType); Assert.Equal(InputHints.AcceptingInput, ((Activity)activity).InputHint); // Add a magic code to the adapter adapter.AddUserToken(ConnectionName, activity.ChannelId, activity.Recipient.Id, Token, MagicCode); }) .Send(MagicCode) .AssertReply("Logged in.") .StartTestAsync(); }
public async Task AutoSaveStateMiddleware_Chain() { var storage = new MemoryStorage(); // setup userstate var userState = new UserState(storage); var userProperty = userState.CreateProperty <int>("userCount"); // setup convState var convState = new ConversationState(storage); var convProperty = convState.CreateProperty <int>("convCount"); var bss = new AutoSaveStateMiddleware() .Add(userState) .Add(convState); var adapter = new TestAdapter(TestAdapter.CreateConversation(TestContext.TestName)) .Use(bss); const int USER_INITITAL_COUNT = 100; const int CONVERSATION_INITIAL_COUNT = 10; BotCallbackHandler botLogic = async(context, cancellationToken) => { // get userCount and convCount from botStateSet var userCount = await userProperty.GetAsync(context, () => USER_INITITAL_COUNT).ConfigureAwait(false); var convCount = await convProperty.GetAsync(context, () => CONVERSATION_INITIAL_COUNT).ConfigureAwait(false); if (context.Activity.Type == ActivityTypes.Message) { if (context.Activity.Text == "get userCount") { await context.SendActivityAsync(context.Activity.CreateReply($"{userCount}")); } else if (context.Activity.Text == "get convCount") { await context.SendActivityAsync(context.Activity.CreateReply($"{convCount}")); } } // increment userCount and set property using accessor. To be saved later by AutoSaveStateMiddleware userCount++; await userProperty.SetAsync(context, userCount); // increment convCount and set property using accessor. To be saved later by AutoSaveStateMiddleware convCount++; await convProperty.SetAsync(context, convCount); }; await new TestFlow(adapter, botLogic) .Send("test1") .Send("get userCount") .AssertReply((USER_INITITAL_COUNT + 1).ToString()) .Send("get userCount") .AssertReply((USER_INITITAL_COUNT + 2).ToString()) .Send("get convCount") .AssertReply((CONVERSATION_INITIAL_COUNT + 3).ToString()) .StartTestAsync(); // new adapter on new conversation var bss2 = new AutoSaveStateMiddleware() .Add(userState) .Add(convState); adapter = new TestAdapter(new ConversationReference { ChannelId = "test", ServiceUrl = "https://test.com", User = new ChannelAccount("user1", "User1"), Bot = new ChannelAccount("bot", "Bot"), Conversation = new ConversationAccount(false, "convo2", "Conversation2"), }) .Use(bss2); await new TestFlow(adapter, botLogic) .Send("get userCount") .AssertReply((USER_INITITAL_COUNT + 4).ToString(), "user count should continue on new conversation") .Send("get convCount") .AssertReply((CONVERSATION_INITIAL_COUNT + 1).ToString(), "conversationCount for conversation2 should be reset") .StartTestAsync(); }
/// <summary> /// Processes an incoming activity. /// </summary> /// <param name="turnContext">Context object containing information for a single turn of conversation with a user.</param> /// <param name="next">The delegate to call to continue the bot middleware pipeline.</param> /// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param> /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns> public async Task OnTurnAsync(ITurnContext turnContext, NextDelegate next, CancellationToken cancellationToken = default(CancellationToken)) { // Grab the conversation data var conversationStateAccessors = _conversationState.CreateProperty <ConversationData>(nameof(ConversationData)); var conversationData = await conversationStateAccessors.GetAsync(turnContext, () => new ConversationData()); string utterance = null; string detectedLanguage = null; if (turnContext == null) { throw new ArgumentNullException(nameof(turnContext)); } if (turnContext.Activity.Type == ActivityTypes.Message) { utterance = ConvertToUtterance(turnContext); if (!String.IsNullOrEmpty(utterance)) { // Detect language if (_configuration["SkipLanguageDetectionAfterInitialChoice"].ToLower() == "false") { detectedLanguage = await _translator.DetectTextRequestAsync(utterance, Consts.SupportedLanguages); if (detectedLanguage != null) { if (detectedLanguage != conversationData.LanguagePreference) { conversationData.LanguageChangeDetected = true; conversationData.LanguagePreference = detectedLanguage; } } } conversationData.UserQuestion = turnContext.Activity.Text; var translate = ShouldTranslateAsync(turnContext, conversationData.LanguagePreference, cancellationToken); if (translate) { if (turnContext.Activity.Type == ActivityTypes.Message) { var specifiedTranslation = Util.GetTranslation(turnContext.Activity.Text, conversationData.LanguagePreference); if (!String.IsNullOrEmpty(specifiedTranslation)) { turnContext.Activity.Text = specifiedTranslation; } else { turnContext.Activity.Text = await _translator.TranslateAsync(turnContext.Activity.Text, _configuration["TranslateTo"], cancellationToken); } conversationData.TranslatedQuestion = turnContext.Activity.Text; } } } turnContext.OnSendActivities(async(newContext, activities, nextSend) => { // Grab the conversation data var conversationStateAccessors = _conversationState.CreateProperty <ConversationData>(nameof(ConversationData)); var conversationData = await conversationStateAccessors.GetAsync(turnContext, () => new ConversationData()); string userLanguage = conversationData.LanguagePreference; bool shouldTranslate = (userLanguage != _configuration["TranslateTo"]); // Translate messages sent to the user to user language if (shouldTranslate) { List <Task> tasks = new List <Task>(); foreach (Activity currentActivity in activities.Where(a => a.Type == ActivityTypes.Message)) { var storedAnswer = currentActivity.Text; conversationData.TranslatedAnswer = storedAnswer; if (!Util.ShouldSkipTranslation(storedAnswer, userLanguage)) // Do not translated the stored non-English answer. { // Always return traditional Chinese to be consistent. var langTranslateTo = (userLanguage == "zh-Hans") ? "zh-Hant" : userLanguage; tasks.Add(TranslateMessageActivityAsync(conversationData, currentActivity.AsMessageActivity(), langTranslateTo, true)); } } if (tasks.Any()) { await Task.WhenAll(tasks).ConfigureAwait(false); } // Log the questions (There are trace activities that pass through and we do not want to log.) if ((conversationData.TranslatedAnswer != null) && !Util.IsDefaultFeedbackMessage(conversationData.TranslatedAnswer, userLanguage)) { if (!String.IsNullOrEmpty(conversationData.UserQuestion)) { await Util.Log(conversationData.UserQuestion, conversationData.UserAnswer, conversationData.TranslatedQuestion, conversationData.TranslatedAnswer, userLanguage); } } } return(await nextSend()); }); turnContext.OnUpdateActivity(async(newContext, activity, nextUpdate) => { // Grab the conversation data var conversationStateAccessors = _conversationState.CreateProperty <ConversationData>(nameof(ConversationData)); var conversationData = await conversationStateAccessors.GetAsync(turnContext, () => new ConversationData()); string userLanguage = conversationData.LanguagePreference; bool shouldTranslate = userLanguage != _configuration["TranslateTo"]; // Translate messages sent to the user to user language if (activity.Type == ActivityTypes.Message) { if (shouldTranslate) { await TranslateMessageActivityAsync(conversationData, activity.AsMessageActivity(), userLanguage, false); } } return(await nextUpdate()); }); } await next(cancellationToken).ConfigureAwait(false); }
public async Task BasicChoicePrompt() { var dialogs = new DialogSet(); dialogs.Add("test-prompt", new ChoicePrompt(Culture.English) { Style = ListStyle.Inline }); var promptOptions = new ChoicePromptOptions { Choices = new List <Choice> { new Choice { Value = "red" }, new Choice { Value = "green" }, new Choice { Value = "blue" }, } }; dialogs.Add("test", new WaterfallStep[] { async(dc, args, next) => { await dc.PromptAsync("test-prompt", "favorite color?", promptOptions); }, async(dc, args, next) => { var choiceResult = (ChoiceResult)args; await dc.Context.SendActivityAsync($"Bot received the choice '{choiceResult.Value.Value}'."); await dc.EndAsync(); } } ); ConversationState convoState = new ConversationState(new MemoryStorage()); var testProperty = convoState.CreateProperty <Dictionary <string, object> >("test", () => new Dictionary <string, object>()); TestAdapter adapter = new TestAdapter() .Use(convoState); await new TestFlow(adapter, async(turnContext) => { var state = await testProperty.GetAsync(turnContext); var dc = dialogs.CreateContext(turnContext, state); await dc.ContinueAsync(); if (!turnContext.Responded) { await dc.BeginAsync("test"); } }) .Send("hello") .AssertReply("favorite color? (1) red, (2) green, or (3) blue") .Send("green") .AssertReply("Bot received the choice 'green'.") .StartTestAsync(); }
public async void LuisDiscoveryTest() { // arrage var expectedIntent = "Sample"; var expectedName = "Sample"; var expectedScore = 100; var luisAppDetail = new LuisAppDetail() { Intent = expectedIntent, Name = expectedName, Score = expectedScore }; var luisDiscoveryResponse = new LuisDiscoveryResponse() { IsSucceded = true, ResultId = 100, LuisAppDetails = new List <LuisAppDetail>() { luisAppDetail } }; var jsonLuisDiscoveryResponse = JsonConvert.SerializeObject(luisDiscoveryResponse); var handlerMock = new Mock <HttpMessageHandler>(MockBehavior.Strict); handlerMock .Protected() .Setup <Task <HttpResponseMessage> >( "SendAsync", ItExpr.IsAny <HttpRequestMessage>(), ItExpr.IsAny <CancellationToken>() ) .ReturnsAsync(new HttpResponseMessage() { StatusCode = HttpStatusCode.OK, Content = new StringContent(jsonLuisDiscoveryResponse), }) .Verifiable(); var httpClient = new HttpClient(handlerMock.Object) { BaseAddress = new Uri("http://localhost/") }; var storage = new MemoryStorage(); var userState = new UserState(storage); var conversationState = new ConversationState(storage); var adapter = new TestAdapter().Use(new AutoSaveStateMiddleware(conversationState)); var dialogState = conversationState.CreateProperty <DialogState>("dialogState"); var dialogs = new DialogSet(dialogState); var steps = new WaterfallStep[] { async(step, cancellationToken) => { await step.Context.SendActivityAsync("response"); // act ILuisRouterService luisRouterService = new LuisRouterService(httpClient, EnvironmentName, ContentRootPath, userState); var result = await luisRouterService.LuisDiscoveryAsync(step, "TEXT", "APPLICATIONCODE", "ENCRYPTIONKEY"); var item = result.ToList().FirstOrDefault(); // assert Assert.Equal(expectedIntent, item.Intent); Assert.Equal(expectedName, item.Name); Assert.Equal(expectedScore, item.Score); return(Dialog.EndOfTurn); } }; dialogs.Add(new WaterfallDialog( "test", steps)); await new TestFlow(adapter, async(turnContext, cancellationToken) => { var dc = await dialogs.CreateContextAsync(turnContext, cancellationToken); await dc.ContinueDialogAsync(cancellationToken); if (!turnContext.Responded) { await dc.BeginDialogAsync("test", null, cancellationToken); } }) .Send("ask") .AssertReply("response") .StartTestAsync(); }
public async Task CallDialogDefinedInParentComponent() { var convoState = new ConversationState(new MemoryStorage()); var dialogState = convoState.CreateProperty <DialogState>("dialogState"); var adapter = new TestAdapter() .Use(new AutoSaveStateMiddleware(convoState)); var options = new Dictionary <string, string> { { "value", "test" } }; var childComponent = new ComponentDialog("childComponent"); var childSteps = new WaterfallStep[] { async(step, ct) => { await step.Context.SendActivityAsync("Child started."); return(await step.BeginDialogAsync("parentDialog", options)); }, async(step, ct) => { Assert.AreEqual("test", (string)step.Result); await step.Context.SendActivityAsync("Child finished."); return(await step.EndDialogAsync()); }, }; childComponent.AddDialog(new WaterfallDialog( "childDialog", childSteps)); var parentComponent = new ComponentDialog("parentComponent"); parentComponent.AddDialog(childComponent); var parentSteps = new WaterfallStep[] { async(step, dc) => { var stepOptions = step.Options as IDictionary <string, string>; Assert.IsNotNull(stepOptions); Assert.IsTrue(stepOptions.ContainsKey("value")); await step.Context.SendActivityAsync($"Parent called with: {stepOptions["value"]}"); return(await step.EndDialogAsync(stepOptions["value"])); }, }; parentComponent.AddDialog(new WaterfallDialog( "parentDialog", parentSteps)); await new TestFlow(adapter, async(turnContext, cancellationToken) => { var dialogs = new DialogSet(dialogState); dialogs.Add(parentComponent); var dc = await dialogs.CreateContextAsync(turnContext, cancellationToken); var results = await dc.ContinueDialogAsync(cancellationToken); if (results.Status == DialogTurnStatus.Empty) { await dc.BeginDialogAsync("parentComponent", null, cancellationToken); } else if (results.Status == DialogTurnStatus.Complete) { var value = (int)results.Result; await turnContext.SendActivityAsync(MessageFactory.Text("Done"), cancellationToken); } }) .Send("Hi") .AssertReply("Child started.") .AssertReply("Parent called with: test") .AssertReply("Child finished.") .StartTestAsync(); }
protected override async Task OnMessageActivityAsync(ITurnContext <IMessageActivity> turnContext, CancellationToken cancellationToken) { Logger.LogInformation("Running dialog with Message Activity."); if (turnContext.Activity.Type == ActivityTypes.Message) { string temp1 = turnContext.Activity.ChannelData.ToString(); string boolean = "false"; Logger.LogInformation(temp1.Length.ToString()); int len = temp1.Length; if (len > 70) { if (temp1.Substring(70, 4) != null) { boolean = temp1.Substring(70, 4); } Logger.LogInformation(temp1.Substring(70, 4)); if (boolean.Equals("true")) { boolean = "True"; } else { boolean = "True"; } Logger.LogInformation(boolean); bool entry = System.Convert.ToBoolean(boolean); if (entry) { JToken commandToken = JToken.Parse(turnContext.Activity.Value.ToString()); string command = commandToken["action"].Value <string>(); string commandPrompt = command; if (commandPrompt.Equals("order")) { string[] paths = { ".", "Cards", "orderCard.json" }; string fullPath = Path.Combine(paths); var welcomeCard = CreateAdaptiveCardAttachment(fullPath); var response = CreateResponse(turnContext.Activity, welcomeCard); await turnContext.SendActivityAsync(response, cancellationToken); } else if (command.ToLowerInvariant() == "inventory") { string[] paths = { ".", "Cards", "InventoryCard.json" }; string fullPath = Path.Combine(paths); var welcomeCard = CreateAdaptiveCardAttachment(fullPath); var response = CreateResponse(turnContext.Activity, welcomeCard); await turnContext.SendActivityAsync(response, cancellationToken); } else if (command.ToLowerInvariant() == "somethingelse") { commandPrompt = "somethingelse"; await Dialog.Run(turnContext, ConversationState.CreateProperty <DialogState>("DialogState"), cancellationToken); } else if (command.ToLowerInvariant() == "ordernumber") { string[] paths = { ".", "Cards", "orderNumberCard.json" }; string fullPath = Path.Combine(paths); var welcomeCard = CreateAdaptiveCardAttachment(fullPath); var response = CreateResponse(turnContext.Activity, welcomeCard); await turnContext.SendActivityAsync(response, cancellationToken); } else if (command.ToLowerInvariant() == "upsordernumber") { string[] paths = { ".", "Cards", "upsOrderNumberCard.json" }; string fullPath = Path.Combine(paths); var welcomeCard = CreateAdaptiveCardAttachment(fullPath); var response = CreateResponse(turnContext.Activity, welcomeCard); await turnContext.SendActivityAsync(response, cancellationToken); } else if (command.ToLowerInvariant() == "trackingnumber") { string[] paths = { ".", "Cards", "trackingNumberCard.json" }; string fullPath = Path.Combine(paths); var welcomeCard = CreateAdaptiveCardAttachment(fullPath); var response = CreateResponse(turnContext.Activity, welcomeCard); await turnContext.SendActivityAsync(response, cancellationToken); } else if (command.ToLowerInvariant() == "trackingnumber") { string[] paths = { ".", "Cards", "trackingNumberCard.json" }; string fullPath = Path.Combine(paths); var welcomeCard = CreateAdaptiveCardAttachment(fullPath); var response = CreateResponse(turnContext.Activity, welcomeCard); await turnContext.SendActivityAsync(response, cancellationToken); } else if (command.ToLowerInvariant() == "skunumber") { string[] paths = { ".", "Cards", "skuNumberCard.json" }; string fullPath = Path.Combine(paths); var welcomeCard = CreateAdaptiveCardAttachment(fullPath); var response = CreateResponse(turnContext.Activity, welcomeCard); await turnContext.SendActivityAsync(response, cancellationToken); } else if (command.ToLowerInvariant() == "ponumber") { string[] paths = { ".", "Cards", "poNumberCard.json" }; string fullPath = Path.Combine(paths); var welcomeCard = CreateAdaptiveCardAttachment(fullPath); var response = CreateResponse(turnContext.Activity, welcomeCard); await turnContext.SendActivityAsync(response, cancellationToken); } else { await turnContext.SendActivityAsync($"I'm sorry, I didn't understand that. Please try again", cancellationToken : cancellationToken); } } } else { await Dialog.Run(turnContext, ConversationState.CreateProperty <DialogState>("DialogState"), cancellationToken); } } // Run the Dialog with the new message Activity. }
public async void GetTokenTest() { // arrage var expectedToken = "TOKEN"; var identityResponse = new IdentityResponse() { token = expectedToken }; var jsonIdentityResponse = JsonConvert.SerializeObject(identityResponse); var handlerMock = new Mock <HttpMessageHandler>(MockBehavior.Strict); handlerMock .Protected() .Setup <Task <HttpResponseMessage> >( "SendAsync", ItExpr.IsAny <HttpRequestMessage>(), ItExpr.IsAny <CancellationToken>() ) .ReturnsAsync(new HttpResponseMessage() { StatusCode = HttpStatusCode.OK, Content = new StringContent(jsonIdentityResponse), }) .Verifiable(); var httpClient = new HttpClient(handlerMock.Object) { BaseAddress = new Uri("http://localhost/") }; var storage = new MemoryStorage(); var userState = new UserState(storage); var conversationState = new ConversationState(storage); var adapter = new TestAdapter().Use(new AutoSaveStateMiddleware(conversationState)); var dialogState = conversationState.CreateProperty <DialogState>("dialogState"); var dialogs = new DialogSet(dialogState); var steps = new WaterfallStep[] { async(step, cancellationToken) => { await step.Context.SendActivityAsync("response"); // act ILuisRouterService luisRouterService = new LuisRouterService(httpClient, EnvironmentName, ContentRootPath, userState); await luisRouterService.GetTokenAsync(step, "encrypted"); string token = await luisRouterService.TokenPreference.GetAsync(step.Context, () => { return(string.Empty); }); // assert Assert.Equal(expectedToken, token); return(Dialog.EndOfTurn); } }; dialogs.Add(new WaterfallDialog( "test", steps)); await new TestFlow(adapter, async(turnContext, cancellationToken) => { var dc = await dialogs.CreateContextAsync(turnContext, cancellationToken); await dc.ContinueDialogAsync(cancellationToken); if (!turnContext.Responded) { await dc.BeginDialogAsync("test", null, cancellationToken); } }) .Send("ask") .AssertReply("response") .StartTestAsync(); }
public async Task ChoicePrompt() { var dialogs = new DialogSet(); dialogs.Add("test-prompt", new Dialogs.ChoicePrompt(Culture.English) { Style = ListStyle.Inline }); var promptOptions = new ChoicePromptOptions { Choices = new List <Choice> { new Choice { Value = "red" }, new Choice { Value = "green" }, new Choice { Value = "blue" }, }, RetryPromptString = "I didn't catch that. Select a color from the list." }; dialogs.Add("test", new WaterfallStep[] { async(dc, args, next) => { await dc.PromptAsync("test-prompt", "favorite color?", promptOptions); }, async(dc, args, next) => { var choiceResult = (ChoiceResult)args; await dc.Context.SendActivityAsync($"Bot received the choice '{choiceResult.Value.Value}'."); await dc.EndAsync(); } } ); var activities = TranscriptUtilities.GetFromTestContext(TestContext); var convState = new ConversationState(new MemoryStorage()); var testProperty = convState.CreateProperty <Dictionary <string, object> >("test"); var adapter = new TestAdapter() .Use(convState); await new TestFlow(adapter, async(turnContext, cancellationToken) => { if (turnContext.Activity.Type == ActivityTypes.Message) { var state = await testProperty.GetAsync(turnContext, () => new Dictionary <string, object>()); var dc = dialogs.CreateContext(turnContext, state); await dc.ContinueAsync(); if (!turnContext.Responded) { await dc.BeginAsync("test"); } } }) .Test(activities) .StartTestAsync(); }
// This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { // Reading settings Settings.MicrosoftAppId = Configuration.GetSection("MicrosoftAppId")?.Value; Settings.MicrosoftAppPassword = Configuration.GetSection("MicrosoftAppPassword")?.Value; Settings.BotConversationStorageConnectionString = Configuration.GetSection("BotConversationStorageConnectionString")?.Value; Settings.BotConversationStorageKey = Configuration.GetSection("BotConversationStorageKey")?.Value; Settings.BotConversationStorageDatabaseId = Configuration.GetSection("BotConversationStorageDatabaseId")?.Value; Settings.BotConversationStorageUserCollection = Configuration.GetSection("BotConversationStorageUserCollection")?.Value; Settings.BotConversationStorageConversationCollection = Configuration.GetSection("BotConversationStorageConversationCollection")?.Value; Settings.LuisAppId01 = Configuration.GetSection("LuisAppId01")?.Value; Settings.LuisName01 = Configuration.GetSection("LuisName01")?.Value; Settings.LuisAuthoringKey01 = Configuration.GetSection("LuisAuthoringKey01")?.Value; Settings.LuisEndpoint01 = Configuration.GetSection("LuisEndpoint01")?.Value; Settings.QnAKbId01 = Configuration.GetSection("QnAKbId01")?.Value; Settings.QnAName01 = Configuration.GetSection("QnAName01")?.Value; Settings.QnAEndpointKey01 = Configuration.GetSection("QnAEndpointKey01")?.Value; Settings.QnAHostname01 = Configuration.GetSection("QnAHostname01")?.Value; Settings.TotalBestNextAnswers = Configuration.GetSection("TotalBestNextAnswers")?.Value; Settings.HighConfidenceThreshold = Configuration.GetSection("threshold1")?.Value.Split(":".ToCharArray(), StringSplitOptions.RemoveEmptyEntries).ToList(); Settings.MediumConfidenceThreshold = Configuration.GetSection("threshold2")?.Value.Split(":".ToCharArray(), StringSplitOptions.RemoveEmptyEntries).ToList(); Settings.LowConfidenceThreshold = Configuration.GetSection("threshold3")?.Value.Split(":".ToCharArray(), StringSplitOptions.RemoveEmptyEntries).ToList(); Settings.TotalBestNextAnswers = Configuration.GetSection("TotalBestNextAnswers")?.Value; // Add Application Insights services into service collection services.AddApplicationInsightsTelemetry(); // Add the standard telemetry client services.AddSingleton <IBotTelemetryClient, BotTelemetryClient>(); // Add ASP middleware to store the HTTP body, mapped with bot activity key, in the httpcontext.items // This will be picked by the TelemetryBotIdInitializer services.AddTransient <TelemetrySaveBodyASPMiddleware>(); // Add telemetry initializer that will set the correlation context for all telemetry items services.AddSingleton <ITelemetryInitializer, OperationCorrelationTelemetryInitializer>(); // Add telemetry initializer that sets the user ID and session ID (in addition to other // bot-specific properties, such as activity ID) services.AddSingleton <ITelemetryInitializer, TelemetryBotIdInitializer>(); // Adding storage CosmosDbStorage userstorage = new CosmosDbStorage(new CosmosDbStorageOptions { AuthKey = Settings.BotConversationStorageKey, CollectionId = Settings.BotConversationStorageUserCollection, CosmosDBEndpoint = new Uri(Settings.BotConversationStorageConnectionString), DatabaseId = Settings.BotConversationStorageDatabaseId, }); CosmosDbStorage conversationstorage = new CosmosDbStorage(new CosmosDbStorageOptions { AuthKey = Settings.BotConversationStorageKey, CollectionId = Settings.BotConversationStorageConversationCollection, CosmosDBEndpoint = new Uri(Settings.BotConversationStorageConnectionString), DatabaseId = Settings.BotConversationStorageDatabaseId, }); var userState = new UserState(userstorage); var conversationState = new ConversationState(conversationstorage); services.AddSingleton(userState); services.AddSingleton(conversationState); // Adding MVC compatibility services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2); // Adding the credential provider to be used with the Bot Framework Adapter services.AddSingleton <ICredentialProvider, ConfigurationCredentialProvider>(); // Adding the channel provider to be used with the Bot Framework Adapter services.AddSingleton <IChannelProvider, ConfigurationChannelProvider>(); // Adding the Bot Framework Adapter with error handling enabled services.AddSingleton <IBotFrameworkHttpAdapter, AdapterWithErrorHandler>(); // Adding middlewares services.AddSingleton(new AutoSaveStateMiddleware(userState, conversationState)); services.AddSingleton(new ShowTypingMiddleware()); // Adding accessors services.AddSingleton(sp => { // We need to grab the conversationState we added on the options in the previous step var options = sp.GetRequiredService <IOptions <BotFrameworkOptions> >().Value; if (options == null) { throw new InvalidOperationException("BotFrameworkOptions must be configured prior to setting up the State Accessors"); } var luisServices = new Dictionary <string, LuisRecognizer>(); var app = new LuisApplication(Settings.LuisAppId01, Settings.LuisAuthoringKey01, Settings.LuisEndpoint01); var recognizer = new LuisRecognizer(app); luisServices.Add(Settings.LuisName01, recognizer); var qnaEndpoint = new QnAMakerEndpoint() { KnowledgeBaseId = Settings.QnAKbId01, EndpointKey = Settings.QnAEndpointKey01, Host = Settings.QnAHostname01, }; var qnaOptions = new QnAMakerOptions { ScoreThreshold = 0.3F }; var qnaServices = new Dictionary <string, QnAMaker>(); var qnaMaker = new QnAMaker(qnaEndpoint, qnaOptions); qnaServices.Add(Settings.QnAName01, qnaMaker); // Create the custom state accessor. // State accessors enable other components to read and write individual properties of state. var accessors = new BotAccessors(loggerFactory, conversationState, userState, luisServices, qnaServices) { ConversationDialogState = conversationState.CreateProperty <DialogState>("DialogState"), LastSearchPreference = userState.CreateProperty <string>("LastSearchPreference"), LastAnswerPreference = userState.CreateProperty <string>("LastAnswerPreference"), IsAuthenticatedPreference = userState.CreateProperty <bool>("IsAu`thenticatedPreference") }; return(accessors); }); services.AddTransient <IBot, BotAppBot>(); }
/// <summary> /// Runs dialog system in the context of an ITurnContext. /// </summary> /// <param name="context">turn context.</param> /// <param name="cancellationToken">cancelation token.</param> /// <returns>result of the running the logic against the activity.</returns> public async Task <DialogManagerResult> OnTurnAsync(ITurnContext context, CancellationToken cancellationToken = default(CancellationToken)) { BotStateSet botStateSet = new BotStateSet(); ConversationState conversationState = this.ConversationState ?? context.TurnState.Get <ConversationState>() ?? throw new ArgumentNullException($"{nameof(ConversationState)} is not found in the turn context. Have you called adapter.UseState() with a configured ConversationState object?"); UserState userState = this.UserState ?? context.TurnState.Get <UserState>(); if (conversationState != null) { botStateSet.Add(conversationState); } if (userState != null) { botStateSet.Add(userState); } // create property accessors var lastAccessProperty = conversationState.CreateProperty <DateTime>(LASTACCESS); var lastAccess = await lastAccessProperty.GetAsync(context, () => DateTime.UtcNow, cancellationToken : cancellationToken).ConfigureAwait(false); // Check for expired conversation var now = DateTime.UtcNow; if (this.ExpireAfter.HasValue && (DateTime.UtcNow - lastAccess) >= TimeSpan.FromMilliseconds((double)this.ExpireAfter)) { // Clear conversation state await conversationState.ClearStateAsync(context, cancellationToken : cancellationToken).ConfigureAwait(false); } lastAccess = DateTime.UtcNow; await lastAccessProperty.SetAsync(context, lastAccess, cancellationToken : cancellationToken).ConfigureAwait(false); // get dialog stack var dialogsProperty = conversationState.CreateProperty <DialogState>(DIALOGS); DialogState dialogState = await dialogsProperty.GetAsync(context, () => new DialogState(), cancellationToken : cancellationToken).ConfigureAwait(false); // Create DialogContext var dc = new DialogContext(this.dialogSet, context, dialogState); // set DSM configuration dc.SetStateConfiguration(this.StateConfiguration ?? DialogStateManager.CreateStandardConfiguration(conversationState, userState)); // load scopes await dc.GetState().LoadAllScopesAsync(cancellationToken).ConfigureAwait(false); DialogTurnResult turnResult = null; if (dc.ActiveDialog == null) { // start root dialog turnResult = await dc.BeginDialogAsync(this.rootDialogId, cancellationToken : cancellationToken).ConfigureAwait(false); } else { // Continue execution // - This will apply any queued up interruptions and execute the current/next step(s). turnResult = await dc.ContinueDialogAsync(cancellationToken : cancellationToken).ConfigureAwait(false); if (turnResult.Status == DialogTurnStatus.Empty) { // restart root dialog turnResult = await dc.BeginDialogAsync(this.rootDialogId, cancellationToken : cancellationToken).ConfigureAwait(false); } } // save all state scopes to their respective stores. await dc.GetState().SaveAllChangesAsync(cancellationToken).ConfigureAwait(false); // save botstate changes await botStateSet.SaveAllChangesAsync(dc.Context, false, cancellationToken).ConfigureAwait(false); // send trace of memory var snapshot = dc.GetState().GetMemorySnapshot(); var traceActivity = (Activity)Activity.CreateTraceActivity("BotState", "https://www.botframework.com/schemas/botState", snapshot, "Bot State"); await dc.Context.SendActivityAsync(traceActivity).ConfigureAwait(false); return(new DialogManagerResult() { TurnResult = turnResult }); }
public void ConfigureServices(IServiceCollection services) { IStorage dataStore; var environment = Configuration.GetSection("environment")?.Value == "production" ? "production" : "development"; _isProduction = environment == "production"; var secretKey = Configuration.GetSection("botFileSecret")?.Value; var botFilePath = Configuration.GetSection("botFilePath")?.Value; if (!File.Exists(botFilePath)) { throw new FileNotFoundException($"The .bot configuration file was not found. botFilePath: {botFilePath}"); } // Loads .bot configuration file and adds a singleton that your Bot can access through dependency injection. var botConfig = BotConfiguration.Load(@".\PasswordBot.bot", secretKey); services.AddSingleton(sp => botConfig ?? throw new InvalidOperationException($"The .bot configuration file could not be loaded. botFilePath: {botFilePath}")); // The Memory Storage used here is for local bot debugging only. When the bot // is restarted, everything stored in memory will be gone. if (!_isProduction) { dataStore = new MemoryStorage(); } else { // //Storage configuration name or ID from the .bot file. const string StorageConfigurationId = "blob"; var blobConfig = botConfig.FindServiceByNameOrId(StorageConfigurationId); if (!(blobConfig is BlobStorageService blobStorageConfig)) { throw new InvalidOperationException($"The .bot file does not contain an blob storage with name '{StorageConfigurationId}'."); } //Default container name. const string DefaultBotContainer = "passwordnotificationbot"; var storageContainer = string.IsNullOrWhiteSpace(blobStorageConfig.Container) ? DefaultBotContainer : blobStorageConfig.Container; dataStore = new Microsoft.Bot.Builder.Azure.AzureBlobStorage(blobStorageConfig.ConnectionString, storageContainer); } // Create PasswordNotificationState object. // The Password Notification State object is where we persist anything at the notification-scope. // Note: It's independent of any user or conversation. PasswordNotificationState notificationsState = new PasswordNotificationState(dataStore); // Make it available to our bot services.AddSingleton(sp => notificationsState); ConversationState conversationState = new ConversationState(dataStore); UserState userState = new UserState(dataStore); DialogState conversationDialogState = new DialogState(); services.AddBot <PasswordBot>(options => { // Retrieve current endpoint. 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); options.ChannelProvider = new ConfigurationChannelProvider(Configuration); // Creates a logger for the application to use. ILogger logger = _loggerFactory.CreateLogger <PasswordBot>(); // 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."); }; }); services.AddSingleton <StateBotAccessors>(sp => { return(new StateBotAccessors(conversationDialogState, conversationState, userState) { // The dialogs will need a state store accessor. Creating it here once (on-demand) allows the dependency injection // to hand it to our IBot class that is create per-request. ConversationDataAccessor = conversationState.CreateProperty <ConversationData>(StateBotAccessors.ConversationDataName), UserProfileAccessor = userState.CreateProperty <UserProfile>(StateBotAccessors.UserProfileName), ConversationDialogStateAccessor = conversationState.CreateProperty <DialogState>(StateBotAccessors.ConversationDialogName), }); }); services.AddSingleton(sp => { 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}'."); } return((EndpointService)service); }); }
/// <summary> /// Runs dialog system in the context of an ITurnContext. /// </summary> /// <param name="context">turn context.</param> /// <param name="cancellationToken">Cancellation token.</param> /// <returns>result of the running the logic against the activity.</returns> public async Task <DialogManagerResult> OnTurnAsync(ITurnContext context, CancellationToken cancellationToken = default) { var botStateSet = new BotStateSet(); // Preload TurnState with DM TurnState. foreach (var pair in InitialTurnState) { context.TurnState.Set(pair.Key, pair.Value); } // register DialogManager with TurnState. context.TurnState.Set(this); if (ConversationState == null) { ConversationState = context.TurnState.Get <ConversationState>() ?? throw new InvalidOperationException($"Unable to get an instance of {nameof(ConversationState)} from turnContext."); } else { context.TurnState.Set(ConversationState); } botStateSet.Add(ConversationState); if (UserState == null) { UserState = context.TurnState.Get <UserState>(); } else { context.TurnState.Set(UserState); } if (UserState != null) { botStateSet.Add(UserState); } // create property accessors var lastAccessProperty = ConversationState.CreateProperty <DateTime>(LastAccess); var lastAccess = await lastAccessProperty.GetAsync(context, () => DateTime.UtcNow, cancellationToken).ConfigureAwait(false); // Check for expired conversation if (ExpireAfter.HasValue && (DateTime.UtcNow - lastAccess) >= TimeSpan.FromMilliseconds((double)ExpireAfter)) { // Clear conversation state await ConversationState.ClearStateAsync(context, cancellationToken).ConfigureAwait(false); } lastAccess = DateTime.UtcNow; await lastAccessProperty.SetAsync(context, lastAccess, cancellationToken).ConfigureAwait(false); // get dialog stack var dialogsProperty = ConversationState.CreateProperty <DialogState>(_dialogStateProperty); var dialogState = await dialogsProperty.GetAsync(context, () => new DialogState(), cancellationToken).ConfigureAwait(false); // Create DialogContext var dc = new DialogContext(Dialogs, context, dialogState); // Call the common dialog "continue/begin" execution pattern shared with the classic RunAsync extension method var turnResult = await DialogExtensions.InternalRunAsync(context, _rootDialogId, dc, StateConfiguration, cancellationToken).ConfigureAwait(false); // save BotState changes await botStateSet.SaveAllChangesAsync(dc.Context, false, cancellationToken).ConfigureAwait(false); return(new DialogManagerResult { TurnResult = turnResult }); }
public void MainDialog_ConstructorValid() { var convoState = new ConversationState(new MemoryStorage()); var dialogStateProperty = convoState.CreateProperty <DialogState>("dialogstate"); var mainDialog = new MainDialog(dialogStateProperty); }
public void initializeAccessors() { ConversationDataAccessor = ConversationState.CreateProperty <ConversationData>(ConversationDataId); DialogStateAccessor = ConversationState.CreateProperty <DialogState>(DialogStateId); UserProfileAccessor = UserState.CreateProperty <UserProfile>(UserProfileId); }
/// <summary> /// Runs dialog system in the context of an ITurnContext. /// </summary> /// <param name="context">turn context.</param> /// <param name="cancellationToken">cancelation token.</param> /// <returns>result of the running the logic against the activity.</returns> public async Task <DialogManagerResult> OnTurnAsync(ITurnContext context, CancellationToken cancellationToken = default(CancellationToken)) { var botStateSet = new BotStateSet(); // preload turnstate with DM turnstate foreach (var pair in this.TurnState) { context.TurnState.Set(pair.Key, pair.Value); } if (this.ConversationState == null) { this.ConversationState = context.TurnState.Get <ConversationState>() ?? throw new ArgumentNullException(nameof(this.ConversationState)); } else { context.TurnState.Set(this.ConversationState); } botStateSet.Add(this.ConversationState); if (this.UserState == null) { this.UserState = context.TurnState.Get <UserState>(); } if (this.UserState != null) { botStateSet.Add(this.UserState); } // create property accessors var lastAccessProperty = ConversationState.CreateProperty <DateTime>(LASTACCESS); var lastAccess = await lastAccessProperty.GetAsync(context, () => DateTime.UtcNow, cancellationToken : cancellationToken).ConfigureAwait(false); // Check for expired conversation var now = DateTime.UtcNow; if (this.ExpireAfter.HasValue && (DateTime.UtcNow - lastAccess) >= TimeSpan.FromMilliseconds((double)this.ExpireAfter)) { // Clear conversation state await ConversationState.ClearStateAsync(context, cancellationToken : cancellationToken).ConfigureAwait(false); } lastAccess = DateTime.UtcNow; await lastAccessProperty.SetAsync(context, lastAccess, cancellationToken : cancellationToken).ConfigureAwait(false); // get dialog stack var dialogsProperty = ConversationState.CreateProperty <DialogState>(this.dialogStateProperty); DialogState dialogState = await dialogsProperty.GetAsync(context, () => new DialogState(), cancellationToken : cancellationToken).ConfigureAwait(false); // Create DialogContext var dc = new DialogContext(this.Dialogs, context, dialogState); // get the dialogstatemanager configuration var dialogStateManager = new DialogStateManager(dc); await dialogStateManager.LoadAllScopesAsync(cancellationToken).ConfigureAwait(false); dc.Context.TurnState.Add(dialogStateManager); DialogTurnResult turnResult = null; // Loop as long as we are getting valid OnError handled we should continue executing the actions for the turn. // // NOTE: We loop around this block because each pass through we either complete the turn and break out of the loop // or we have had an exception AND there was an OnError action which captured the error. We need to continue the // turn based on the actions the OnError handler introduced. while (true) { try { if (dc.ActiveDialog == null) { // start root dialog turnResult = await dc.BeginDialogAsync(this.rootDialogId, cancellationToken : cancellationToken).ConfigureAwait(false); } else { // Continue execution // - This will apply any queued up interruptions and execute the current/next step(s). turnResult = await dc.ContinueDialogAsync(cancellationToken : cancellationToken).ConfigureAwait(false); if (turnResult.Status == DialogTurnStatus.Empty) { // restart root dialog turnResult = await dc.BeginDialogAsync(this.rootDialogId, cancellationToken : cancellationToken).ConfigureAwait(false); } } // turn successfully completed, break the loop break; } catch (Exception err) { // fire error event, bubbling from the leaf. var handled = await dc.EmitEventAsync(DialogEvents.Error, err, bubble : true, fromLeaf : true, cancellationToken : cancellationToken).ConfigureAwait(false); if (!handled) { // error was NOT handled, throw the exception and end the turn. (This will trigger the Adapter.OnError handler and end the entire dialog stack) throw; } } } // save all state scopes to their respective botState locations. await dc.GetState().SaveAllChangesAsync(cancellationToken).ConfigureAwait(false); // save botstate changes await botStateSet.SaveAllChangesAsync(dc.Context, false, cancellationToken).ConfigureAwait(false); // send trace of memory var snapshot = dc.GetState().GetMemorySnapshot(); var traceActivity = (Activity)Activity.CreateTraceActivity("BotState", "https://www.botframework.com/schemas/botState", snapshot, "Bot State"); await dc.Context.SendActivityAsync(traceActivity).ConfigureAwait(false); return(new DialogManagerResult() { TurnResult = turnResult }); }
public async Task Handle(ITurnContext <IMessageActivity> turnContext, CancellationToken cancellationToken) { await _qnADialog.RunAsync(turnContext, _conversationState.CreateProperty <DialogState>(nameof(DialogState)), cancellationToken); }