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)); } }
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)); } }
// 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"); } }
// 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)); } }
// 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(); }