public void Test_SettingFilter_Temperature() { var state = new AutomotiveSkillState(); state.Entities.Add("SETTING", new List <string> { "temperature" }); state.Entities.Add("AMOUNT", new List <string> { "21" }); state.Entities.Add("UNIT", new List <string> { "degrees" }); settingFilter.PostProcessSettingName(state); Assert.AreEqual("Temperature", state.Changes[0].SettingName); Assert.AreEqual(21, state.Changes[0].Amount.Amount); }
/// <summary> /// Top level processing, is the user trying to check or change a setting?. /// </summary> /// <param name="sc">Step Context.</param> /// <param name="cancellationToken">Cancellation Token.</param> /// <returns>Dialog Turn Result.</returns> public async Task <DialogTurnResult> ProcessSetting(WaterfallStepContext sc, CancellationToken cancellationToken = default(CancellationToken)) { var state = await Accessor.GetAsync(sc.Context, () => new AutomotiveSkillState()); var luisResult = state.VehicleSettingsLuisResult; var topIntent = luisResult?.TopIntent().intent; switch (topIntent.Value) { case settingsLuis.Intent.VEHICLE_SETTINGS_CHANGE: case settingsLuis.Intent.VEHICLE_SETTINGS_DECLARATIVE: // Perform post-processing on the entities, if it's declarative we indicate for special processing (opposite of the condition they've expressed) settingFilter.PostProcessSettingName(state, topIntent.Value == settingsLuis.Intent.VEHICLE_SETTINGS_DECLARATIVE ? true : false); // Perform content logic and remove entities that don't make sense settingFilter.ApplyContentLogic(state); var settingNames = state.GetUniqueSettingNames(); if (!settingNames.Any()) { // missing setting name await sc.Context.SendActivityAsync(ResponseManager.GetResponse(VehicleSettingsResponses.VehicleSettingsMissingSettingName)); return(await sc.EndDialogAsync()); } else if (settingNames.Count() > 1) { // If we have more than one setting name matching prompt the user to choose var options = new PromptOptions() { Choices = new List <Choice>(), }; for (var i = 0; i < settingNames.Count; ++i) { var item = settingNames[i]; var synonyms = new List <string> { item, (i + 1).ToString() }; synonyms.AddRange(settingList.GetAlternativeNamesForSetting(item)); var choice = new Choice() { Value = item, Synonyms = synonyms, }; options.Choices.Add(choice); } var cardModel = new AutomotiveCardModel() { ImageUrl = GetSettingCardImageUri(FallbackSettingImageFileName) }; var card = new Card(GetDivergedCardName(sc.Context, "AutomotiveCard"), cardModel); options.Prompt = ResponseManager.GetCardResponse(VehicleSettingsResponses.VehicleSettingsSettingNameSelection, card, tokens: null); // Default Text property is clumsy for speech options.Prompt.Speak = SpeechUtility.ListToSpeechReadyString(options); // Workaround. In teams, prompt will be changed to HeroCard and adaptive card could not be shown. So send them separatly if (Channel.GetChannelId(sc.Context) == Channels.Msteams) { await sc.Context.SendActivityAsync(options.Prompt); options.Prompt = null; } return(await sc.PromptAsync(Actions.SettingNameSelectionPrompt, options)); } else { // Only one setting detected so move on to next stage return(await sc.NextAsync()); } case settingsLuis.Intent.VEHICLE_SETTINGS_CHECK: await sc.Context.SendActivityAsync(sc.Context.Activity.CreateReply("The skill doesn't support checking vehicle settings quite yet!")); return(await sc.EndDialogAsync(true, cancellationToken)); default: await sc.Context.SendActivityAsync(ResponseManager.GetResponse(VehicleSettingsResponses.VehicleSettingsOutOfDomain)); return(await sc.EndDialogAsync(true, cancellationToken)); } }
/// <summary> /// Top level processing, is the user trying to check or change a setting?. /// </summary> /// <param name="sc">Step Context.</param> /// <param name="cancellationToken">Cancellation Token.</param> /// <returns>Dialog Turn Result.</returns> public async Task <DialogTurnResult> ProcessSetting(WaterfallStepContext sc, CancellationToken cancellationToken = default(CancellationToken)) { var state = await Accessor.GetAsync(sc.Context, () => new AutomotiveSkillState()); var luisResult = state.VehicleSettingsLuisResult; var topIntent = luisResult?.TopIntent().intent; switch (topIntent.Value) { case Luis.VehicleSettings.Intent.VEHICLE_SETTINGS_CHANGE: case Luis.VehicleSettings.Intent.VEHICLE_SETTINGS_DECLARATIVE: // Perform post-processing on the entities, if it's declarative we indicate for special processing (opposite of the condition they've expressed) settingFilter.PostProcessSettingName(state, topIntent.Value == Luis.VehicleSettings.Intent.VEHICLE_SETTINGS_DECLARATIVE ? true : false); // Perform content logic and remove entities that don't make sense settingFilter.ApplyContentLogic(state); var settingNames = state.GetUniqueSettingNames(); if (!settingNames.Any()) { // missing setting name await sc.Context.SendActivityAsync(ResponseManager.GetResponse(VehicleSettingsResponses.VehicleSettingsMissingSettingName)); return(await sc.EndDialogAsync()); } else if (settingNames.Count() > 1) { // If we have more than one setting name matching prompt the user to choose var options = new PromptOptions() { Choices = new List <Choice>(), }; for (var i = 0; i < settingNames.Count; ++i) { var item = settingNames[i]; List <string> synonyms = new List <string>(); synonyms.Add(item); synonyms.Add((i + 1).ToString()); synonyms.AddRange(settingList.GetAlternativeNamesForSetting(item)); var choice = new Choice() { Value = item, Synonyms = synonyms, }; options.Choices.Add(choice); } options.Prompt = ResponseManager.GetResponse(VehicleSettingsResponses.VehicleSettingsSettingNameSelection); var card = new ThumbnailCard { Images = new List <CardImage> { new CardImage(GetSettingCardImageUri(FallbackSettingImageFileName)) }, Text = options.Prompt.Text, Buttons = options.Choices.Select(choice => new CardAction(ActionTypes.ImBack, choice.Value, value: choice.Value)).ToList(), }; options.Prompt.Attachments.Add(card.ToAttachment()); // Default Text property is clumsy for speech options.Prompt.Speak = $"{options.Prompt.Text} {GetSpeakableOptions(options.Choices)}"; return(await sc.PromptAsync(Actions.SettingNameSelectionPrompt, options)); } else { // Only one setting detected so move on to next stage return(await sc.NextAsync()); } case Luis.VehicleSettings.Intent.VEHICLE_SETTINGS_CHECK: await sc.Context.SendActivityAsync(sc.Context.Activity.CreateReply("The skill doesn't support checking vehicle settings quite yet!")); return(await sc.EndDialogAsync(true, cancellationToken)); default: await sc.Context.SendActivityAsync(ResponseManager.GetResponse(VehicleSettingsResponses.VehicleSettingsOutOfDomain)); return(await sc.EndDialogAsync(true, cancellationToken)); } }
/// <summary> /// Top level processing, is the user trying to check or change a setting?. /// </summary> /// <param name="sc">Step Context.</param> /// <param name="cancellationToken">Cancellation Token.</param> /// <returns>Dialog Turn Result.</returns> public async Task <DialogTurnResult> ProcessSetting(WaterfallStepContext sc, CancellationToken cancellationToken = default(CancellationToken)) { var state = await Accessor.GetAsync(sc.Context); // Ensure we don't have state from a previous instantiation state.Changes.Clear(); state.Entities.Clear(); var luisResult = state.VehicleSettingsLuisResult; var topIntent = luisResult?.TopIntent().intent; switch (topIntent.Value) { case Luis.VehicleSettings.Intent.VEHICLE_SETTINGS_CHANGE: case Luis.VehicleSettings.Intent.VEHICLE_SETTINGS_DECLARATIVE: // Process the LUIS result and add entities to the State accessors for ease of access if (luisResult.Entities.AMOUNT != null) { state.Entities.Add(nameof(luisResult.Entities.AMOUNT), luisResult.Entities.AMOUNT); } if (luisResult.Entities.INDEX != null) { state.Entities.Add(nameof(luisResult.Entities.INDEX), luisResult.Entities.INDEX); } if (luisResult.Entities.SETTING != null) { state.Entities.Add(nameof(luisResult.Entities.SETTING), luisResult.Entities.SETTING); } if (luisResult.Entities.TYPE != null) { state.Entities.Add(nameof(luisResult.Entities.TYPE), luisResult.Entities.TYPE); } if (luisResult.Entities.UNIT != null) { state.Entities.Add(nameof(luisResult.Entities.UNIT), luisResult.Entities.UNIT); } if (luisResult.Entities.VALUE != null) { state.Entities.Add(nameof(luisResult.Entities.VALUE), luisResult.Entities.VALUE); } // Perform post-processing on the entities, if it's declarative we indicate for special processing (opposite of the condition they've expressed) settingFilter.PostProcessSettingName(state, topIntent.Value == Luis.VehicleSettings.Intent.VEHICLE_SETTINGS_DECLARATIVE ? true : false); // Perform content logic and remove entities that don't make sense settingFilter.ApplyContentLogic(state); var settingNames = state.GetUniqueSettingNames(); if (!settingNames.Any()) { // missing setting name await sc.Context.SendActivityAsync(sc.Context.Activity.CreateReply(VehicleSettingsResponses.VehicleSettingsMissingSettingName)); return(await sc.EndDialogAsync()); } else if (settingNames.Count() > 1) { // If we have more than one setting name matching prompt the user to choose var options = new PromptOptions() { Choices = new List <Choice>(), }; for (var i = 0; i < settingNames.Count; ++i) { var item = settingNames[i]; var choice = new Choice() { Value = item, Synonyms = new List <string> { (i + 1).ToString(), item }, }; options.Choices.Add(choice); } var card = new HeroCard { Images = new List <CardImage> { new CardImage(GetSettingCardImageUri("settingcog.jpg")) }, Text = "Please choose from one of the available settings shown below", Buttons = options.Choices.Select(choice => new CardAction(ActionTypes.ImBack, choice.Value, value: choice.Value)).ToList(), }; options.Prompt = (Activity)MessageFactory.Attachment(card.ToAttachment()); return(await sc.PromptAsync(Actions.SettingNameSelectionPrompt, options)); } else { // Only one setting detected so move on to next stage return(await sc.NextAsync()); } case Luis.VehicleSettings.Intent.VEHICLE_SETTINGS_CHECK: await sc.Context.SendActivityAsync(sc.Context.Activity.CreateReply("The skill doesn't support checking vehicle settings quite yet!")); return(await sc.EndDialogAsync(true, cancellationToken)); default: await sc.Context.SendActivityAsync(sc.Context.Activity.CreateReply(VehicleSettingsResponses.VehicleSettingsOutOfDomain)); return(await sc.EndDialogAsync(true, cancellationToken)); } }
/// <summary> /// Top level processing, is the user trying to check or change a setting?. /// </summary> /// <param name="sc">Step Context.</param> /// <param name="cancellationToken">Cancellation Token.</param> /// <returns>Dialog Turn Result.</returns> public async Task <DialogTurnResult> ProcessSettingAsync(WaterfallStepContext sc, CancellationToken cancellationToken = default(CancellationToken)) { var state = await Accessor.GetAsync(sc.Context, () => new AutomotiveSkillState(), cancellationToken); var skillResult = sc.Context.TurnState.Get <SettingsLuis>(StateProperties.SettingsLuisResultKey); bool isDeclarative = skillResult?.TopIntent().intent == SettingsLuis.Intent.VEHICLE_SETTINGS_DECLARATIVE; // Perform post-processing on the entities, if it's declarative we indicate for special processing (opposite of the condition they've expressed) settingFilter.PostProcessSettingName(state, isDeclarative); // Perform content logic and remove entities that don't make sense settingFilter.ApplyContentLogic(state); var settingNames = state.GetUniqueSettingNames(); if (!settingNames.Any()) { // missing setting name await sc.Context.SendActivityAsync(TemplateManager.GenerateActivityForLocale(VehicleSettingsResponses.VehicleSettingsMissingSettingName), cancellationToken); return(await sc.EndDialogAsync(cancellationToken : cancellationToken)); } else if (settingNames.Count() > 1) { // If we have more than one setting name matching prompt the user to choose var options = new PromptOptions() { Choices = new List <Choice>(), }; for (var i = 0; i < settingNames.Count; ++i) { var item = settingNames[i]; var synonyms = new List <string> { item, (i + 1).ToString() }; synonyms.AddRange(settingList.GetAlternativeNamesForSetting(item)); var choice = new Choice() { Value = item, Synonyms = synonyms, }; options.Choices.Add(choice); } var cardModel = new AutomotiveCardModel() { ImageUrl = GetSettingCardImageUri(FallbackSettingImageFileName) }; var card = TemplateManager.GenerateActivityForLocale(GetDivergedCardName(sc.Context, "AutomotiveCard"), cardModel); options.Prompt = TemplateManager.GenerateActivityForLocale(VehicleSettingsResponses.VehicleSettingsSettingNameSelection); options.Prompt.Attachments = card.Attachments; // Default Text property is clumsy for speech options.Prompt.Speak = SpeechUtility.ListToSpeechReadyString(options); // Workaround. In teams, prompt will be changed to HeroCard and adaptive card could not be shown. So send them separatly if (Channel.GetChannelId(sc.Context) == Channels.Msteams) { await sc.Context.SendActivityAsync(options.Prompt, cancellationToken); options.Prompt = null; } return(await sc.PromptAsync(Actions.SettingNameSelectionPrompt, options, cancellationToken)); } else { // Only one setting detected so move on to next stage return(await sc.NextAsync(cancellationToken : cancellationToken)); } }