Example #1
0
        /// <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));
            }
        }
Example #2
0
        /// <summary>
        /// Once we have a setting we need to process the corresponding value.
        /// </summary>
        /// <param name="sc">Step Context.</param>
        /// <returns>Dialog Turn Result.</returns>
        private async Task <DialogTurnResult> ProcessVehicleSettingsChange(WaterfallStepContext sc, CancellationToken cancellationToken = default(CancellationToken))
        {
            var state = await Accessor.GetAsync(sc.Context);

            if (state.Changes.Any())
            {
                var settingValues = state.GetUniqueSettingValues();
                if (!settingValues.Any())
                {
                    // This shouldn't happen because the SettingFilter would just add all possible values to let the user select from them.
                    await sc.Context.SendActivityAsync(ResponseManager.GetResponse(VehicleSettingsResponses.VehicleSettingsMissingSettingValue));

                    return(await sc.EndDialogAsync());
                }
                else
                {
                    // We have found multiple setting values, which we need to prompt the user to resolve
                    if (settingValues.Count() > 1)
                    {
                        var settingName = state.Changes.First().SettingName;
                        var setting     = this.settingList.FindSetting(settingName);

                        // If an image filename is provided we'll use it otherwise fall back to the generic car one
                        var imageName = setting.ImageFileName ?? FallbackSettingImageFileName;

                        // If we have more than one setting value matching, prompt the user to choose
                        var options = new PromptOptions()
                        {
                            Choices = new List <Choice>(),
                        };

                        for (var i = 0; i < settingValues.Count; ++i)
                        {
                            var item     = settingValues[i];
                            var synonyms = new List <string>
                            {
                                item,
                                (i + 1).ToString()
                            };
                            synonyms.AddRange(settingList.GetAlternativeNamesForSettingValue(settingName, item));
                            var choice = new Choice()
                            {
                                Value    = item,
                                Synonyms = synonyms,
                            };
                            options.Choices.Add(choice);
                        }

                        var promptReplacements = new StringDictionary {
                            { "settingName", settingName }
                        };
                        var cardModel = new AutomotiveCardModel()
                        {
                            ImageUrl = GetSettingCardImageUri(imageName)
                        };

                        var card = new Card(GetDivergedCardName(sc.Context, "AutomotiveCard"), cardModel);

                        options.Prompt = ResponseManager.GetCardResponse(VehicleSettingsResponses.VehicleSettingsSettingValueSelection, card, promptReplacements);

                        // Default Text property is clumsy for speech
                        options.Prompt.Speak = SpeechUtility.ListToSpeechReadyString(options.Prompt);

                        // 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.SettingValueSelectionPrompt, options));
                    }
                    else
                    {
                        // We only have one setting value so proceed to next step
                        return(await sc.NextAsync());
                    }
                }
            }
            else
            {
                // No setting value was understood
                await sc.Context.SendActivityAsync(ResponseManager.GetResponse(VehicleSettingsResponses.VehicleSettingsOutOfDomain));

                return(await sc.EndDialogAsync());
            }
        }
        /// <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));
            }
        }