private async Task <DialogTurnResult> PromptForRecipientAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
        {
            try
            {
                var state = await PhoneStateAccessor.GetAsync(stepContext.Context, cancellationToken : cancellationToken);

                var contactProvider = GetContactProvider(state);
                await contactFilter.FilterAsync(state, contactProvider);

                var hasRecipient = await CheckRecipientAndExplainFailureToUserAsync(stepContext.Context, state, cancellationToken);

                if (hasRecipient)
                {
                    return(await stepContext.NextAsync(cancellationToken : cancellationToken));
                }

                var prompt = TemplateManager.GenerateActivity(OutgoingCallResponses.RecipientPrompt);
                return(await stepContext.PromptAsync(DialogIds.RecipientPrompt, new PromptOptions { Prompt = prompt }, cancellationToken));
            }
            catch (Exception ex)
            {
                await HandleDialogExceptionsAsync(stepContext, ex, cancellationToken);

                return(new DialogTurnResult(DialogTurnStatus.Cancelled, CommonUtil.DialogTurnResultCancelAllDialogs));
            }
        }
        private async Task <bool> ValidatePhoneNumberTypeConfirmationAsync(PromptValidatorContext <bool> promptContext, CancellationToken cancellationToken)
        {
            if (!promptContext.Recognized.Succeeded)
            {
                // The user said neither yes nor no.
                return(false);
            }

            var state = await PhoneStateAccessor.GetAsync(promptContext.Context, cancellationToken : cancellationToken);

            if (promptContext.Recognized.Value)
            {
                // The user said yes.
                state.ContactResult.RequestedPhoneNumberType = state.ContactResult.Matches[0].PhoneNumbers[0].Type;
            }
            else
            {
                // The user said no.
                // We cannot restart the dialog from a validator function,
                // so we have to delay that until the next waterfall step is called.
                state.ClearExceptAuth();
            }

            return(true);
        }
        public async Task OnLogoutAsync(DialogContext dialogContext, CancellationToken cancellationToken)
        {
            var state = await PhoneStateAccessor.GetAsync(dialogContext.Context, cancellationToken : cancellationToken);

            // When the user logs out, remove the login token and all their personal data from the state.
            state.Clear();
        }
        private async Task <DialogTurnResult> AskToSelectPhoneNumberAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
        {
            try
            {
                var state = await PhoneStateAccessor.GetAsync(stepContext.Context, cancellationToken : cancellationToken);

                // If the user said 'no' to the PhoneNumberTypeConfirmation prompt,
                // then the state would have been cleared and we need to restart the whole dialog.
                if (!contactFilter.HasRecipient(state))
                {
                    return(await stepContext.ReplaceDialogAsync(DialogIds.OutgoingCallNoAuth, cancellationToken : cancellationToken));
                }

                await contactFilter.FilterAsync(state, contactProvider : null);

                if (contactFilter.IsPhoneNumberDisambiguated(state))
                {
                    return(await stepContext.NextAsync(cancellationToken : cancellationToken));
                }

                var options = new PromptOptions();
                UpdatePhoneNumberSelectionPromptOptions(options, state);

                return(await stepContext.PromptAsync(DialogIds.PhoneNumberSelection, options, cancellationToken));
            }
            catch (Exception ex)
            {
                await HandleDialogExceptionsAsync(stepContext, ex, cancellationToken);

                return(new DialogTurnResult(DialogTurnStatus.Cancelled, CommonUtil.DialogTurnResultCancelAllDialogs));
            }
        }
        private async Task <DialogTurnResult> AskToSelectContactAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
        {
            try
            {
                var state = await PhoneStateAccessor.GetAsync(stepContext.Context, cancellationToken : cancellationToken);

                await contactFilter.FilterAsync(state, contactProvider : null);

                if (contactFilter.IsContactDisambiguated(state))
                {
                    return(await stepContext.NextAsync(cancellationToken : cancellationToken));
                }

                var options = new PromptOptions();
                UpdateContactSelectionPromptOptions(options, state);

                return(await stepContext.PromptAsync(DialogIds.ContactSelection, options, cancellationToken));
            }
            catch (Exception ex)
            {
                await HandleDialogExceptionsAsync(stepContext, ex, cancellationToken);

                return(new DialogTurnResult(DialogTurnStatus.Cancelled, CommonUtil.DialogTurnResultCancelAllDialogs));
            }
        }
示例#6
0
        protected async Task <DialogTurnResult> AfterGetAuthToken(WaterfallStepContext sc, CancellationToken cancellationToken = default(CancellationToken))
        {
            try
            {
                if (sc.Result is ProviderTokenResponse providerTokenResponse)
                {
                    var state = await PhoneStateAccessor.GetAsync(sc.Context);

                    state.Token = providerTokenResponse.TokenResponse.Token;

                    var provider = providerTokenResponse.AuthenticationProvider;
                    switch (provider)
                    {
                    case OAuthProvider.AzureAD:
                        state.SourceOfContacts = ContactSource.Microsoft;
                        break;

                    case OAuthProvider.Google:
                        state.SourceOfContacts = ContactSource.Google;
                        break;

                    default:
                        throw new Exception($"The authentication provider \"{provider.ToString()}\" is not supported by the Phone skill.");
                    }
                }

                return(await sc.NextAsync());
            }
            catch (Exception ex)
            {
                await HandleDialogExceptions(sc, ex);

                return(new DialogTurnResult(DialogTurnStatus.Cancelled, CommonUtil.DialogTurnResultCancelAllDialogs));
            }
        }
示例#7
0
        // Helpers
        protected async Task GetLuisResult(DialogContext dc)
        {
            if (dc.Context.Activity.Type == ActivityTypes.Message)
            {
                var state = await PhoneStateAccessor.GetAsync(dc.Context);

                state.LuisResult = await RunLuis <PhoneLuis>(dc.Context, "phone");
            }
        }
示例#8
0
        // Helpers
        protected async Task GetLuisResultAsync(DialogContext dc, CancellationToken cancellationToken)
        {
            if (dc.Context.Activity.Type == ActivityTypes.Message)
            {
                var state = await PhoneStateAccessor.GetAsync(dc.Context, cancellationToken : cancellationToken);

                state.LuisResult = dc.Context.TurnState.Get <PhoneLuis>(StateProperties.PhoneLuisResultKey);
            }
        }
        private async Task <DialogTurnResult> ExecuteCallAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
        {
            try
            {
                var state = await PhoneStateAccessor.GetAsync(stepContext.Context, cancellationToken : cancellationToken);

                await contactFilter.FilterAsync(state, contactProvider : null);

                var templateId   = OutgoingCallResponses.ExecuteCall;
                var tokens       = new Dictionary <string, object>();
                var outgoingCall = new OutgoingCall
                {
                    Number = state.PhoneNumber,
                };
                if (state.ContactResult.Matches.Count == 1)
                {
                    tokens["contactOrPhoneNumber"] = state.ContactResult.Matches[0].Name;
                    outgoingCall.Contact           = state.ContactResult.Matches[0];
                }
                else
                {
                    tokens["contactOrPhoneNumber"] = state.PhoneNumber;
                }

                if (state.ContactResult.RequestedPhoneNumberType.Any() &&
                    state.ContactResult.Matches.Count == 1 &&
                    state.ContactResult.Matches[0].PhoneNumbers.Count == 1)
                {
                    templateId = OutgoingCallResponses.ExecuteCallWithPhoneNumberType;
                    tokens["phoneNumberType"] = GetSpeakablePhoneNumberType(state.ContactResult.Matches[0].PhoneNumbers[0].Type);
                }

                var response = TemplateManager.GenerateActivity(templateId, tokens);
                await stepContext.Context.SendActivityAsync(response, cancellationToken);

                await SendEventAsync(stepContext, outgoingCall, cancellationToken);

                ActionResult actionResult = null;
                if (state.IsAction)
                {
                    actionResult = new ActionResult()
                    {
                        ActionSuccess = true
                    };
                }

                state.Clear();
                return(await stepContext.EndDialogAsync(actionResult, cancellationToken));
            }
            catch (Exception ex)
            {
                await HandleDialogExceptionsAsync(stepContext, ex, cancellationToken);

                return(new DialogTurnResult(DialogTurnStatus.Cancelled, CommonUtil.DialogTurnResultCancelAllDialogs));
            }
        }
        private async Task <bool> ValidateRecipientAsync(PromptValidatorContext <string> promptContext, CancellationToken cancellationToken)
        {
            var state = await PhoneStateAccessor.GetAsync(promptContext.Context, cancellationToken : cancellationToken);

            var phoneResult = promptContext.Context.TurnState.Get <PhoneLuis>(StateProperties.PhoneLuisResultKey);

            contactFilter.OverrideEntities(state, phoneResult);

            var contactProvider = GetContactProvider(state);
            await contactFilter.FilterAsync(state, contactProvider);

            return(await CheckRecipientAndExplainFailureToUserAsync(promptContext.Context, state, cancellationToken));
        }
        private async Task <DialogTurnResult> ConfirmChangeOfPhoneNumberTypeAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
        {
            try
            {
                var state = await PhoneStateAccessor.GetAsync(stepContext.Context, cancellationToken : cancellationToken);

                var(isFiltered, hasPhoneNumberOfRequestedType) = await contactFilter.FilterAsync(state, contactProvider : null);

                if (hasPhoneNumberOfRequestedType ||
                    !state.ContactResult.Matches.Any() ||
                    !state.ContactResult.RequestedPhoneNumberType.Any())
                {
                    return(await stepContext.NextAsync(cancellationToken : cancellationToken));
                }

                var notFoundTokens = new Dictionary <string, object>()
                {
                    { "contact", state.ContactResult.Matches[0].Name },
                    { "phoneNumberType", GetSpeakablePhoneNumberType(state.ContactResult.RequestedPhoneNumberType) },
                };
                var response = TemplateManager.GenerateActivity(OutgoingCallResponses.ContactHasNoPhoneNumberOfRequestedType, notFoundTokens);
                await stepContext.Context.SendActivityAsync(response, cancellationToken);

                if (state.ContactResult.Matches[0].PhoneNumbers.Count != 1)
                {
                    return(await stepContext.NextAsync(cancellationToken : cancellationToken));
                }

                var confirmationTokens = new Dictionary <string, object>()
                {
                    { "phoneNumberType", GetSpeakablePhoneNumberType(state.ContactResult.Matches[0].PhoneNumbers[0].Type) },
                };
                var options = new PromptOptions();
                options.Prompt = TemplateManager.GenerateActivity(OutgoingCallResponses.ConfirmAlternativePhoneNumberType, confirmationTokens);
                return(await stepContext.PromptAsync(DialogIds.PhoneNumberTypeConfirmation, options, cancellationToken));
            }
            catch (Exception ex)
            {
                await HandleDialogExceptionsAsync(stepContext, ex, cancellationToken);

                return(new DialogTurnResult(DialogTurnStatus.Cancelled, CommonUtil.DialogTurnResultCancelAllDialogs));
            }
        }
示例#12
0
        // This method is called by any waterfall step that throws an exception to ensure consistency
        protected async Task HandleDialogExceptions(WaterfallStepContext sc, Exception ex)
        {
            // send trace back to emulator
            var trace = new Activity(type: ActivityTypes.Trace, text: $"DialogException: {ex.Message}, StackTrace: {ex.StackTrace}");
            await sc.Context.SendActivityAsync(trace);

            // log exception
            TelemetryClient.TrackException(ex, new Dictionary <string, string> {
                { nameof(sc.ActiveDialog), sc.ActiveDialog?.Id }
            });

            // send error message to bot user
            await sc.Context.SendActivityAsync(ResponseManager.GetResponse(PhoneSharedResponses.ErrorMessage));

            // clear state
            var state = await PhoneStateAccessor.GetAsync(sc.Context);

            state.Clear();
        }
        private async Task <bool> ValidatePhoneNumberChoiceAsync(PromptValidatorContext <FoundChoice> promptContext, CancellationToken cancellationToken)
        {
            var state = await PhoneStateAccessor.GetAsync(promptContext.Context, cancellationToken : cancellationToken);

            if (contactFilter.IsPhoneNumberDisambiguated(state))
            {
                return(true);
            }

            var phoneNumberSelectionResult = await RunLuisAsync <PhoneNumberSelectionLuis>(promptContext.Context, "phoneNumberSelection", cancellationToken);

            contactFilter.OverrideEntities(state, phoneNumberSelectionResult);
            var(isFiltered, _) = await contactFilter.FilterAsync(state, contactProvider : null);

            if (contactFilter.IsPhoneNumberDisambiguated(state))
            {
                return(true);
            }
            else if (isFiltered)
            {
                UpdatePhoneNumberSelectionPromptOptions(promptContext.Options, state);
                return(false);
            }

            var phoneNumberList = state.ContactResult.Matches[0].PhoneNumbers;

            if (promptContext.Recognized.Value != null &&
                promptContext.Recognized.Value.Index >= 0 &&
                promptContext.Recognized.Value.Index < phoneNumberList.Count)
            {
                state.ContactResult.Matches[0].PhoneNumbers = new List <PhoneNumber>()
                {
                    phoneNumberList[promptContext.Recognized.Value.Index]
                };
            }

            return(contactFilter.IsPhoneNumberDisambiguated(state));
        }
        private async Task <bool> ValidateContactChoice(PromptValidatorContext <FoundChoice> promptContext, CancellationToken cancellationToken)
        {
            var state = await PhoneStateAccessor.GetAsync(promptContext.Context);

            if (contactFilter.IsContactDisambiguated(state))
            {
                return(true);
            }

            var contactSelectionResult = await RunLuis <ContactSelectionLuis>(promptContext.Context, "contactSelection");

            contactFilter.OverrideEntities(state, contactSelectionResult);
            var(isFiltered, _) = await contactFilter.Filter(state, contactProvider : null);

            if (contactFilter.IsContactDisambiguated(state))
            {
                return(true);
            }
            else if (isFiltered)
            {
                UpdateContactSelectionPromptOptions(promptContext.Options, state);
                return(false);
            }

            if (promptContext.Recognized.Value != null &&
                promptContext.Recognized.Value.Index >= 0 &&
                promptContext.Recognized.Value.Index < state.ContactResult.Matches.Count)
            {
                state.ContactResult.Matches = new List <ContactCandidate>()
                {
                    state.ContactResult.Matches[promptContext.Recognized.Value.Index]
                };
            }

            return(contactFilter.IsContactDisambiguated(state));
        }
        public async Task OnCancelAsync(DialogContext dialogContext, CancellationToken cancellationToken)
        {
            var state = await PhoneStateAccessor.GetAsync(dialogContext.Context, cancellationToken : cancellationToken);

            state.ClearExceptAuth();
        }
        public async Task OnCancel(DialogContext dialogContext)
        {
            var state = await PhoneStateAccessor.GetAsync(dialogContext.Context);

            state.ClearExceptAuth();
        }