public MarkToDoItemDialog( SkillConfigurationBase services, ResponseManager responseManager, IStatePropertyAccessor <ToDoSkillState> toDoStateAccessor, IStatePropertyAccessor <ToDoSkillUserState> userStateAccessor, IServiceManager serviceManager, IBotTelemetryClient telemetryClient) : base(nameof(MarkToDoItemDialog), services, responseManager, toDoStateAccessor, userStateAccessor, serviceManager, telemetryClient) { TelemetryClient = telemetryClient; var markTask = new WaterfallStep[] { GetAuthToken, AfterGetAuthToken, ClearContext, CollectListTypeForComplete, InitAllTasks, DoMarkTask, }; var doMarkTask = new WaterfallStep[] { CollectTaskIndexForComplete, MarkTaskCompleted, ContinueMarkTask, }; var collectListTypeForComplete = new WaterfallStep[] { AskListType, AfterAskListType, }; var collectTaskIndexForComplete = new WaterfallStep[] { AskTaskIndex, AfterAskTaskIndex, }; var continueMarkTask = new WaterfallStep[] { AskContinueMarkTask, AfterAskContinueMarkTask, }; // Define the conversation flow using a waterfall model. AddDialog(new WaterfallDialog(Action.DoMarkTask, doMarkTask) { TelemetryClient = telemetryClient }); AddDialog(new WaterfallDialog(Action.MarkTaskCompleted, markTask) { TelemetryClient = telemetryClient }); AddDialog(new WaterfallDialog(Action.CollectListTypeForComplete, collectListTypeForComplete) { TelemetryClient = telemetryClient }); AddDialog(new WaterfallDialog(Action.CollectTaskIndexForComplete, collectTaskIndexForComplete) { TelemetryClient = telemetryClient }); AddDialog(new WaterfallDialog(Action.ContinueMarkTask, continueMarkTask) { TelemetryClient = telemetryClient }); // Set starting dialog for component InitialDialogId = Action.MarkTaskCompleted; }
protected async Task <DialogTurnResult> ReadEmail(WaterfallStepContext sc, CancellationToken cancellationToken = default(CancellationToken)) { try { var state = await EmailStateAccessor.GetAsync(sc.Context); var skillOptions = (EmailSkillDialogOptions)sc.Options; sc.Context.Activity.Properties.TryGetValue("OriginText", out var content); var userInput = content != null?content.ToString() : sc.Context.Activity.Text; var luisResult = sc.Context.TurnState.Get <EmailLuis>(StateProperties.EmailLuisResult); var topIntent = luisResult?.TopIntent().intent; var generalLuisResult = sc.Context.TurnState.Get <General>(StateProperties.GeneralLuisResult); var generalTopIntent = generalLuisResult?.TopIntent().intent; if (topIntent == null) { return(await sc.EndDialogAsync(true)); } await DigestFocusEmailAsync(sc); var message = state.Message.FirstOrDefault(); if (message == null) { state.Message.Add(state.MessageList[0]); message = state.Message.FirstOrDefault(); } var promptRecognizerResult = ConfirmRecognizerHelper.ConfirmYesOrNo(userInput, sc.Context.Activity.Locale); if ((topIntent == EmailLuis.Intent.None || topIntent == EmailLuis.Intent.SearchMessages || (topIntent == EmailLuis.Intent.ReadAloud && !IsReadMoreIntent(generalTopIntent, sc.Context.Activity.Text)) || (promptRecognizerResult.Succeeded && promptRecognizerResult.Value == true)) && message != null) { var senderIcon = await GetUserPhotoUrlAsync(sc.Context, message.Sender.EmailAddress); var emailCard = new EmailCardData { Subject = message.Subject, Sender = message.Sender.EmailAddress.Name, EmailContent = message.BodyPreview, EmailLink = message.WebLink, ReceivedDateTime = message?.ReceivedDateTime == null ? CommonStrings.NotAvailable : message.ReceivedDateTime.Value.UtcDateTime.ToDetailRelativeString(state.GetUserTimeZone()), Speak = SpeakHelper.ToSpeechEmailDetailOverallString(message, state.GetUserTimeZone()), SenderIcon = senderIcon }; emailCard = await ProcessRecipientPhotoUrl(sc.Context, emailCard, message.ToRecipients); var tokens = new StringDictionary() { { "EmailDetails", SpeakHelper.ToSpeechEmailDetailString(message, state.GetUserTimeZone()) }, { "EmailDetailsWithContent", SpeakHelper.ToSpeechEmailDetailString(message, state.GetUserTimeZone(), true) }, }; var recipientCard = message.ToRecipients.Count() > 5 ? GetDivergedCardName(sc.Context, "DetailCard_RecipientMoreThanFive") : GetDivergedCardName(sc.Context, "DetailCard_RecipientLessThanFive"); var replyMessage = ResponseManager.GetCardResponse( ShowEmailResponses.ReadOutMessage, new Card("EmailDetailCard", emailCard), tokens, "items", new List <Card>().Append(new Card(recipientCard, emailCard))); // Set email as read. var service = ServiceManager.InitMailService(state.Token, state.GetUserTimeZone(), state.MailSourceType); await service.MarkMessageAsReadAsync(message.Id); await sc.Context.SendActivityAsync(replyMessage); } return(await sc.NextAsync()); } catch (SkillException ex) { await HandleDialogExceptions(sc, ex); return(new DialogTurnResult(DialogTurnStatus.Cancelled, CommonUtil.DialogTurnResultCancelAllDialogs)); } catch (Exception ex) { await HandleDialogExceptions(sc, ex); return(new DialogTurnResult(DialogTurnStatus.Cancelled, CommonUtil.DialogTurnResultCancelAllDialogs)); } }
protected async Task <DialogTurnResult> ShowFilteredEmails(WaterfallStepContext sc, CancellationToken cancellationToken = default(CancellationToken)) { try { var state = await EmailStateAccessor.GetAsync(sc.Context); if (state.MessageList.Count > 0) { if (state.Message.Count == 0) { state.Message.Add(state.MessageList[0]); if (state.MessageList.Count > 1) { var importCount = 0; foreach (var msg in state.MessageList) { if (msg.Importance.HasValue && msg.Importance.Value == Importance.High) { importCount++; } } await ShowMailList(sc, state.MessageList, state.MessageList.Count(), importCount, cancellationToken); return(await sc.NextAsync()); } else if (state.MessageList.Count == 1) { return(await sc.ReplaceDialogAsync(Actions.Read, options : sc.Options)); } } else { return(await sc.ReplaceDialogAsync(Actions.Read, options : sc.Options)); } await sc.Context.SendActivityAsync(ResponseManager.GetResponse(EmailSharedResponses.DidntUnderstandMessage)); return(await sc.EndDialogAsync(true)); } else { await sc.Context.SendActivityAsync(ResponseManager.GetResponse(EmailSharedResponses.EmailNotFound)); } return(await sc.EndDialogAsync(true)); } catch (SkillException ex) { await HandleDialogExceptions(sc, ex); return(new DialogTurnResult(DialogTurnStatus.Cancelled, CommonUtil.DialogTurnResultCancelAllDialogs)); } catch (Exception ex) { await HandleDialogExceptions(sc, ex); return(new DialogTurnResult(DialogTurnStatus.Cancelled, CommonUtil.DialogTurnResultCancelAllDialogs)); } }
public async Task <DialogTurnResult> AfterUpdateUserName(WaterfallStepContext sc, CancellationToken cancellationToken = default(CancellationToken)) { try { var userInput = sc.Result as string; var state = await Accessor.GetAsync(sc.Context); var options = (UpdateUserNameDialogOptions)sc.Options; if (string.IsNullOrEmpty(userInput) && options.Reason != UpdateUserNameDialogOptions.UpdateReason.Initialize) { await sc.Context.SendActivityAsync(ResponseManager.GetResponse(FindContactResponses.UserNotFoundAgain, new StringDictionary() { { "source", state.EventSource == EventSource.Microsoft ? "Outlook Calendar" : "Google Calendar" } })); return(await sc.EndDialogAsync()); } string currentRecipientName = string.IsNullOrEmpty(userInput) ? state.CurrentAttendeeName : userInput; state.CurrentAttendeeName = currentRecipientName; // if it's an email, add to attendee and kepp the state.ConfirmedPerson null if (!string.IsNullOrEmpty(currentRecipientName) && IsEmail(currentRecipientName)) { var attendee = new EventModel.Attendee { DisplayName = currentRecipientName, Address = currentRecipientName }; if (state.Attendees.All(r => r.Address != attendee.Address)) { state.Attendees.Add(attendee); } state.CurrentAttendeeName = string.Empty; state.ConfirmedPerson = null; return(await sc.EndDialogAsync()); } List <CustomizedPerson> unionList = new List <CustomizedPerson>(); if (CreateEventWhiteList.GetMyself(currentRecipientName)) { var me = await GetMe(sc.Context); unionList.Add(new CustomizedPerson(me)); } else { var originPersonList = await GetPeopleWorkWithAsync(sc, currentRecipientName); var originContactList = await GetContactsAsync(sc, currentRecipientName); originPersonList.AddRange(originContactList); var originUserList = new List <PersonModel>(); try { originUserList = await GetUserAsync(sc, currentRecipientName); } catch { // do nothing when get user failed. because can not use token to ensure user use a work account. } (var personList, var userList) = FormatRecipientList(originPersonList, originUserList); // people you work with has the distinct email address has the highest priority if (personList.Count == 1 && personList.First().Emails.Any() && personList.First().Emails.First() != null) { unionList.Add(new CustomizedPerson(personList.First())); } else { personList.AddRange(userList); foreach (var person in personList) { if (unionList.Find(p => p.DisplayName == person.DisplayName) == null) { var personWithSameName = personList.FindAll(p => p.DisplayName == person.DisplayName); if (personWithSameName.Count == 1) { unionList.Add(new CustomizedPerson(personWithSameName.First())); } else { var unionPerson = new CustomizedPerson(personWithSameName.FirstOrDefault()); var curEmailList = new List <ScoredEmailAddress>(); foreach (var sameNamePerson in personWithSameName) { sameNamePerson.Emails.ToList().ForEach(e => { if (!string.IsNullOrEmpty(e)) { curEmailList.Add(new ScoredEmailAddress { Address = e }); } }); } unionPerson.Emails = curEmailList; unionList.Add(unionPerson); } } } } } unionList.RemoveAll(person => !person.Emails.Exists(email => email.Address != null)); unionList.RemoveAll(person => !person.Emails.Any()); state.UnconfirmedPerson = unionList; if (unionList.Count == 0) { return(await sc.ReplaceDialogAsync(Actions.UpdateName, new UpdateUserNameDialogOptions(UpdateUserNameDialogOptions.UpdateReason.NotFound))); } else if (unionList.Count == 1) { state.ConfirmedPerson = unionList.First(); return(await sc.EndDialogAsync()); } else { return(await sc.ReplaceDialogAsync(Actions.SelectPerson, sc.Options, cancellationToken)); } } catch (SkillException skillEx) { await HandleDialogExceptions(sc, skillEx); return(new DialogTurnResult(DialogTurnStatus.Cancelled, CommonUtil.DialogTurnResultCancelAllDialogs)); } catch (Exception ex) { await HandleDialogExceptions(sc, ex); return(new DialogTurnResult(DialogTurnStatus.Cancelled, CommonUtil.DialogTurnResultCancelAllDialogs)); } }
protected async Task <DialogTurnResult> PromptToHandle(WaterfallStepContext sc, CancellationToken cancellationToken = default(CancellationToken)) { try { var state = await EmailStateAccessor.GetAsync(sc.Context); if (state.MessageList.Count == 1) { return(await sc.PromptAsync(Actions.Prompt, new PromptOptions { Prompt = ResponseManager.GetResponse(ShowEmailResponses.ReadOutOnlyOnePrompt) })); } else { return(await sc.PromptAsync(Actions.Prompt, new PromptOptions { Prompt = ResponseManager.GetResponse(ShowEmailResponses.ReadOutPrompt) })); } } catch (Exception ex) { await HandleDialogExceptions(sc, ex); return(new DialogTurnResult(DialogTurnStatus.Cancelled, CommonUtil.DialogTurnResultCancelAllDialogs)); } }
public async Task <DialogTurnResult> AfterConfirmNameList(WaterfallStepContext sc, CancellationToken cancellationToken = default(CancellationToken)) { try { var state = await Accessor.GetAsync(sc.Context); // get name list from sc.result if (sc.Result != null) { sc.Context.Activity.Properties.TryGetValue("OriginText", out var content); var userInput = content != null?content.ToString() : sc.Context.Activity.Text; // if is skip. set the name list to be myself only. if (CreateEventWhiteList.IsSkip(userInput)) { state.AttendeesNameList = new List <string> { CalendarCommonStrings.MyselfConst }; } else if (state.EventSource != EventSource.Other) { if (userInput != null) { var nameList = userInput.Split(CreateEventWhiteList.GetContactNameSeparator(), StringSplitOptions.None) .Select(x => x.Trim()) .Where(x => !string.IsNullOrWhiteSpace(x)) .ToList(); state.AttendeesNameList = nameList; } } } if (state.AttendeesNameList.Any()) { if (state.AttendeesNameList.Count > 1) { var nameString = await GetReadyToSendNameListStringAsync(sc); await sc.Context.SendActivityAsync(ResponseManager.GetResponse(FindContactResponses.BeforeSendingMessage, new StringDictionary() { { "NameList", nameString } })); } // go to loop to go through all the names state.ConfirmAttendeesNameIndex = 0; return(await sc.ReplaceDialogAsync(Actions.LoopNameList, sc.Options, cancellationToken)); } state.AttendeesNameList = new List <string>(); state.CurrentAttendeeName = string.Empty; state.ConfirmAttendeesNameIndex = 0; return(await sc.EndDialogAsync()); } catch (Exception ex) { await HandleDialogExceptions(sc, ex); return(new DialogTurnResult(DialogTurnStatus.Cancelled, CommonUtil.DialogTurnResultCancelAllDialogs)); } }
public async Task <DialogTurnResult> ConfirmEmail(WaterfallStepContext sc, CancellationToken cancellationToken = default(CancellationToken)) { var state = await Accessor.GetAsync(sc.Context); var confirmedPerson = state.ConfirmedPerson; if (confirmedPerson == null) { return(await sc.EndDialogAsync()); } var name = confirmedPerson.DisplayName; if (confirmedPerson.Emails.Count() == 1) { // Highest probability return(await sc.PromptAsync(Actions.TakeFurtherAction, new PromptOptions { Prompt = ResponseManager.GetResponse(FindContactResponses.PromptOneNameOneAddress, new StringDictionary() { { "UserName", name }, { "EmailAddress", confirmedPerson.Emails.First().Address ?? confirmedPerson.UserPrincipalName } }), })); } else { return(await sc.BeginDialogAsync(Actions.SelectEmail)); } }
public override void Initialize() { this.ServiceManager = MockServiceManager.GetCalendarService(); // Initialize service collection Services = new ServiceCollection(); Services.AddSingleton(new BotSettings() { OAuthConnections = new List <OAuthConnection>() { new OAuthConnection() { Name = "Microsoft", Provider = "Microsoft" } } }); Services.AddSingleton(new BotServices()); Services.AddSingleton <IBotTelemetryClient, NullBotTelemetryClient>(); Services.AddSingleton(new UserState(new MemoryStorage())); Services.AddSingleton(new ConversationState(new MemoryStorage())); Services.AddSingleton(new ProactiveState(new MemoryStorage())); Services.AddSingleton(new MicrosoftAppCredentials(string.Empty, string.Empty)); Services.AddSingleton(sp => { var userState = sp.GetService <UserState>(); var conversationState = sp.GetService <ConversationState>(); var proactiveState = sp.GetService <ProactiveState>(); return(new BotStateSet(userState, conversationState)); }); ResponseManager = new ResponseManager( new string[] { "en", "de", "es", "fr", "it", "zh" }, new FindContactResponses(), new ChangeEventStatusResponses(), new CreateEventResponses(), new JoinEventResponses(), new CalendarMainResponses(), new CalendarSharedResponses(), new SummaryResponses(), new TimeRemainingResponses(), new UpdateEventResponses(), new UpcomingEventResponses()); Services.AddSingleton(ResponseManager); Services.AddSingleton <IBackgroundTaskQueue, BackgroundTaskQueue>(); Services.AddSingleton(ServiceManager); Services.AddSingleton <TestAdapter, DefaultTestAdapter>(); Services.AddTransient <MainDialog>(); Services.AddTransient <ChangeEventStatusDialog>(); Services.AddTransient <ConnectToMeetingDialog>(); Services.AddTransient <CreateEventDialog>(); Services.AddTransient <FindContactDialog>(); Services.AddTransient <SummaryDialog>(); Services.AddTransient <TimeRemainingDialog>(); Services.AddTransient <UpcomingEventDialog>(); Services.AddTransient <UpdateEventDialog>(); Services.AddTransient <FindContactDialog>(); Services.AddTransient <IBot, DialogBot <MainDialog> >(); var state = Services.BuildServiceProvider().GetService <ConversationState>(); CalendarStateAccessor = state.CreateProperty <CalendarSkillState>(nameof(CalendarSkillState)); }
public FakeSkill(SkillConfigurationBase services, EndpointService endpointService, ConversationState conversationState, UserState userState, ProactiveState proactiveState, IBotTelemetryClient telemetryClient, IBackgroundTaskQueue backgroundTaskQueue, bool skillMode = false, ResponseManager responseManager = null, ServiceManager serviceManager = null) { _skillMode = skillMode; _services = services ?? throw new ArgumentNullException(nameof(services)); _userState = userState ?? throw new ArgumentNullException(nameof(userState)); _conversationState = conversationState ?? throw new ArgumentNullException(nameof(conversationState)); _proactiveState = proactiveState; _endpointService = endpointService; _backgroundTaskQueue = backgroundTaskQueue; _telemetryClient = telemetryClient ?? throw new ArgumentNullException(nameof(telemetryClient)); _serviceManager = serviceManager ?? new ServiceManager(); if (responseManager == null) { var locales = new string[] { "en-us", "de-de", "es-es", "fr-fr", "it-it", "zh-cn" }; responseManager = new ResponseManager( locales, new SampleAuthResponses(), new MainResponses(), new SharedResponses(), new SampleResponses()); } _responseManager = responseManager; _dialogs = new DialogSet(_conversationState.CreateProperty <DialogState>(nameof(DialogState))); _dialogs.Add(new MainDialog(_services, _responseManager, _conversationState, _userState, _telemetryClient, _serviceManager, _skillMode)); }
private async Task <DialogTurnResult> SendChange(WaterfallStepContext sc, CancellationToken cancellationToken = default(CancellationToken)) { var state = await Accessor.GetAsync(sc.Context); var settingChangeConfirmed = false; // If we skip the ConfirmPrompt due to no confirmation needed then Result will be NULL if (sc.Result == null) { settingChangeConfirmed = true; } else { settingChangeConfirmed = (bool)sc.Result; } if (settingChangeConfirmed) { var change = state.Changes[0]; // If the change involves an amount then we add this to the change event if (change.Amount != null) { var promptReplacements = new StringDictionary { { "settingName", change.SettingName }, { "amount", change.Amount.Amount.ToString() }, { "unit", UnitToString(change.Amount.Unit) }, }; if (change.IsRelativeAmount) { if (change.Amount.Amount < 0) { promptReplacements["increasingDecreasing"] = VehicleSettingsStrings.DECREASING; promptReplacements["amount"] = (-change.Amount.Amount).ToString(); } else { promptReplacements["increasingDecreasing"] = VehicleSettingsStrings.INCREASING; } // Send an event to the device along with the text confirmation await SendActionToDevice(sc, change, promptReplacements); await sc.Context.SendActivityAsync(ResponseManager.GetResponse( VehicleSettingsResponses.VehicleSettingsChangingRelativeAmount, promptReplacements)); } else { // Send an event to the device along with the text confirmation await SendActionToDevice(sc, change, promptReplacements); await sc.Context.SendActivityAsync(ResponseManager.GetResponse( VehicleSettingsResponses.VehicleSettingsChangingAmount, promptReplacements)); } } else { // Nominal (non-numeric) change (e.g., on/off) string promptTemplate; var promptReplacements = new StringDictionary { { "settingName", change.SettingName } }; if (SettingValueToSpeakableIngForm.TryGetValue(change.Value.ToLowerInvariant(), out var valueIngForm)) { promptTemplate = VehicleSettingsResponses.VehicleSettingsChangingValueKnown; promptReplacements["valueIngForm"] = valueIngForm; } else { promptTemplate = VehicleSettingsResponses.VehicleSettingsChangingValue; promptReplacements["value"] = change.Value; } // Send an event to the device along with the text confirmation await SendActionToDevice(sc, change, promptReplacements); await sc.Context.SendActivityAsync(ResponseManager.GetResponse(promptTemplate, promptReplacements)); } } else { await sc.Context.SendActivityAsync(ResponseManager.GetResponse(VehicleSettingsResponses.VehicleSettingsSettingChangeConfirmationDenied)); } return(await sc.EndDialogAsync()); }
public VehicleSettingsDialog( SkillConfigurationBase services, ResponseManager responseManager, IStatePropertyAccessor <AutomotiveSkillState> accessor, IServiceManager serviceManager, IBotTelemetryClient telemetryClient, IHttpContextAccessor httpContext) : base(nameof(VehicleSettingsDialog), services, responseManager, accessor, serviceManager, telemetryClient) { TelemetryClient = telemetryClient; // Initialise supporting LUIS models for followup questions vehicleSettingNameSelectionLuisRecognizer = services.LocaleConfigurations["en"].LuisServices["settings_name"]; vehicleSettingValueSelectionLuisRecognizer = services.LocaleConfigurations["en"].LuisServices["settings_value"]; // Initialise supporting LUIS models for followup questions vehicleSettingNameSelectionLuisRecognizer = services.LocaleConfigurations["en"].LuisServices["settings_name"]; vehicleSettingValueSelectionLuisRecognizer = services.LocaleConfigurations["en"].LuisServices["settings_value"]; // Supporting setting files are stored as embeddded resources Assembly resourceAssembly = typeof(VehicleSettingsDialog).Assembly; var settingFile = resourceAssembly .GetManifestResourceNames() .Where(x => x.Contains(AvailableSettingsFileName)) .First(); var alternativeSettingFileName = resourceAssembly .GetManifestResourceNames() .Where(x => x.Contains(AlternativeSettingsFileName)) .First(); if (string.IsNullOrEmpty(settingFile) || string.IsNullOrEmpty(alternativeSettingFileName)) { throw new FileNotFoundException($"Unable to find Available Setting and/or Alternative Names files in \"{resourceAssembly.FullName}\" assembly."); } settingList = new SettingList(resourceAssembly, settingFile, alternativeSettingFileName); settingFilter = new SettingFilter(settingList); // Setting Change waterfall var processVehicleSettingChangeWaterfall = new WaterfallStep[] { ProcessSetting, ProcessVehicleSettingsChange, ProcessChange, SendChange }; AddDialog(new WaterfallDialog(Actions.ProcessVehicleSettingChange, processVehicleSettingChangeWaterfall) { TelemetryClient = telemetryClient }); // Prompts AddDialog(new ChoicePrompt(Actions.SettingNameSelectionPrompt, SettingNameSelectionValidator, Culture.English) { Style = ListStyle.Inline, ChoiceOptions = new ChoiceFactoryOptions { InlineSeparator = string.Empty, InlineOr = string.Empty, InlineOrMore = string.Empty, IncludeNumbers = true } }); AddDialog(new ChoicePrompt(Actions.SettingValueSelectionPrompt, SettingValueSelectionValidator, Culture.English) { Style = ListStyle.Inline, ChoiceOptions = new ChoiceFactoryOptions { InlineSeparator = string.Empty, InlineOr = string.Empty, InlineOrMore = string.Empty, IncludeNumbers = true } }); AddDialog(new ConfirmPrompt(Actions.SettingConfirmationPrompt)); // Set starting dialog for component InitialDialogId = Actions.ProcessVehicleSettingChange; // Used to resolve image paths (local or hosted) _httpContext = httpContext; }
/// <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) { string 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 string 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]; List <string> synonyms = new List <string>(); synonyms.Add(item); synonyms.Add((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 } }; options.Prompt = ResponseManager.GetResponse(VehicleSettingsResponses.VehicleSettingsSettingValueSelection, promptReplacements); var card = new ThumbnailCard { Text = options.Prompt.Text, Images = new List <CardImage> { new CardImage(GetSettingCardImageUri(imageName)) }, 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.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> 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)); } }
public void ConfigureServices(IServiceCollection services) { // add background task queue services.AddSingleton <IBackgroundTaskQueue, BackgroundTaskQueue>(); services.AddHostedService <QueuedHostedService>(); // Load the connected services from .bot file. var botFilePath = Configuration.GetSection("botFilePath")?.Value; var botFileSecret = Configuration.GetSection("botFileSecret")?.Value; var botConfig = BotConfiguration.Load(botFilePath ?? @".\CalendarSkill.bot", botFileSecret); services.AddSingleton(sp => botConfig ?? throw new InvalidOperationException($"The .bot config file could not be loaded.")); // Use Application Insights services.AddBotApplicationInsights(botConfig); // Initializes your bot service clients and adds a singleton that your Bot can access through dependency injection. var parameters = Configuration.GetSection("parameters")?.Get <string[]>(); var configuration = Configuration.GetSection("configuration")?.GetChildren()?.ToDictionary(x => x.Key, y => y.Value as object); var supportedProviders = Configuration.GetSection("supportedProviders")?.Get <string[]>(); var languageModels = Configuration.GetSection("languageModels").Get <Dictionary <string, Dictionary <string, string> > >(); var connectedServices = new SkillConfiguration(botConfig, languageModels, supportedProviders, parameters, configuration); services.AddSingleton <SkillConfigurationBase>(sp => connectedServices); var supportedLanguages = languageModels.Select(l => l.Key).ToArray(); var responses = new IResponseIdCollection[] { new FindContactResponses(), new ChangeEventStatusResponses(), new CreateEventResponses(), new JoinEventResponses(), new CalendarMainResponses(), new CalendarSharedResponses(), new SummaryResponses(), new TimeRemainingResponses(), new UpdateEventResponses(), }; var responseManager = new ResponseManager(responses, supportedLanguages); // Register bot responses for all supported languages. services.AddSingleton(sp => responseManager); // Initialize Bot State var cosmosDbService = botConfig.Services.FirstOrDefault(s => s.Type == ServiceTypes.CosmosDB) ?? throw new Exception("Please configure your CosmosDb service in your .bot file."); var cosmosDb = cosmosDbService as CosmosDbService; var cosmosOptions = new CosmosDbStorageOptions() { CosmosDBEndpoint = new Uri(cosmosDb.Endpoint), AuthKey = cosmosDb.Key, CollectionId = cosmosDb.Collection, DatabaseId = cosmosDb.Database, }; var dataStore = new CosmosDbStorage(cosmosOptions); var userState = new UserState(dataStore); var conversationState = new ConversationState(dataStore); var proactiveState = new ProactiveState(dataStore); services.AddSingleton(dataStore); services.AddSingleton(userState); services.AddSingleton(conversationState); services.AddSingleton(proactiveState); services.AddSingleton(new BotStateSet(userState, conversationState)); var environment = _isProduction ? "production" : "development"; var service = botConfig.Services.FirstOrDefault(s => s.Type == ServiceTypes.Endpoint && s.Name == environment); if (!(service is EndpointService endpointService)) { throw new InvalidOperationException($"The .bot file does not contain an endpoint with name '{environment}'."); } services.AddSingleton(endpointService); // Initialize calendar service client services.AddSingleton <IServiceManager, ServiceManager>(); // Add the bot with options services.AddBot <CalendarSkill>(options => { options.CredentialProvider = new SimpleCredentialProvider(endpointService.AppId, endpointService.AppPassword); // Telemetry Middleware (logs activity messages in Application Insights) var sp = services.BuildServiceProvider(); var telemetryClient = sp.GetService <IBotTelemetryClient>(); var appInsightsLogger = new TelemetryLoggerMiddleware(telemetryClient, logPersonalInformation: true); options.Middleware.Add(appInsightsLogger); // Catches any errors that occur during a conversation turn and logs them to AppInsights. options.OnTurnError = async(context, exception) => { CultureInfo.CurrentUICulture = new CultureInfo(context.Activity.Locale); await context.SendActivityAsync(responseManager.GetResponse(CalendarSharedResponses.CalendarErrorMessage)); await context.SendActivityAsync(new Activity(type: ActivityTypes.Trace, text: $"Calendar Skill Error: {exception.Message} | {exception.StackTrace}")); telemetryClient.TrackExceptionEx(exception, context.Activity); }; // Transcript Middleware (saves conversation history in a standard format) var storageService = botConfig.Services.FirstOrDefault(s => s.Type == ServiceTypes.BlobStorage) ?? throw new Exception("Please configure your Azure Storage service in your .bot file."); var blobStorage = storageService as BlobStorageService; var transcriptStore = new AzureBlobTranscriptStore(blobStorage.ConnectionString, blobStorage.Container); var transcriptMiddleware = new TranscriptLoggerMiddleware(transcriptStore); options.Middleware.Add(transcriptMiddleware); // Typing Middleware (automatically shows typing when the bot is responding/working) var typingMiddleware = new ShowTypingMiddleware(); options.Middleware.Add(typingMiddleware); options.Middleware.Add(new AutoSaveStateMiddleware(userState, conversationState)); var defaultLocale = Configuration.GetSection("defaultLocale").Get <string>(); options.Middleware.Add(new SetLocaleMiddleware(defaultLocale ?? "en")); }); }
public override void Initialize() { // Initialize mock service manager ServiceManager = new MockServiceManager(); // Initialize service collection Services = new ServiceCollection(); Services.AddSingleton(new BotSettings() { OAuthConnections = new List <OAuthConnection>() { new OAuthConnection() { Name = "Microsoft", Provider = "Microsoft" } } }); Services.AddSingleton(new BotServices() { CognitiveModelSets = new Dictionary <string, CognitiveModelSet> { { "en", new CognitiveModelSet() { LuisServices = new Dictionary <string, ITelemetryRecognizer> { { "general", new MockGeneralLuisRecognizer() }, { "email", new MockEmailLuisRecognizer( new ForwardEmailUtterances(), new ReplyEmailUtterances(), new DeleteEmailUtterances(), new SendEmailUtterances(), new ShowEmailUtterances()) } } } } } }); Services.AddSingleton <IBotTelemetryClient, NullBotTelemetryClient>(); Services.AddSingleton(new UserState(new MemoryStorage())); Services.AddSingleton(new ConversationState(new MemoryStorage())); Services.AddSingleton(new ProactiveState(new MemoryStorage())); Services.AddSingleton(new MicrosoftAppCredentials(string.Empty, string.Empty)); Services.AddSingleton(sp => { var userState = sp.GetService <UserState>(); var conversationState = sp.GetService <ConversationState>(); var proactiveState = sp.GetService <ProactiveState>(); return(new BotStateSet(userState, conversationState)); }); ResponseManager = new ResponseManager( new string[] { "en", "de", "es", "fr", "it", "zh" }, new FindContactResponses(), new DeleteEmailResponses(), new ForwardEmailResponses(), new EmailMainResponses(), new ReplyEmailResponses(), new SendEmailResponses(), new EmailSharedResponses(), new ShowEmailResponses()); Services.AddSingleton(ResponseManager); Services.AddSingleton <IBackgroundTaskQueue, BackgroundTaskQueue>(); Services.AddSingleton <IServiceManager>(ServiceManager); Services.AddSingleton <TestAdapter, DefaultTestAdapter>(); Services.AddTransient <MainDialog>(); Services.AddTransient <DeleteEmailDialog>(); Services.AddTransient <FindContactDialog>(); Services.AddTransient <ForwardEmailDialog>(); Services.AddTransient <ReplyEmailDialog>(); Services.AddTransient <SendEmailDialog>(); Services.AddTransient <ShowEmailDialog>(); Services.AddTransient <IBot, DialogBot <MainDialog> >(); ConfigData.GetInstance().MaxDisplaySize = 3; ConfigData.GetInstance().MaxReadSize = 3; }
public ForwardEmailDialog( SkillConfigurationBase services, ResponseManager responseManager, IStatePropertyAccessor <EmailSkillState> emailStateAccessor, IStatePropertyAccessor <DialogState> dialogStateAccessor, IServiceManager serviceManager, IBotTelemetryClient telemetryClient) : base(nameof(ForwardEmailDialog), services, responseManager, emailStateAccessor, dialogStateAccessor, serviceManager, telemetryClient) { TelemetryClient = telemetryClient; var forwardEmail = new WaterfallStep[] { IfClearContextStep, GetAuthToken, AfterGetAuthToken, SetDisplayConfig, CollectSelectedEmail, AfterCollectSelectedEmail, CollectRecipient, CollectAdditionalText, AfterCollectAdditionalText, ConfirmBeforeSending, ConfirmAllRecipient, ForwardEmail, }; var showEmail = new WaterfallStep[] { PagingStep, ShowEmails, }; var collectRecipients = new WaterfallStep[] { PromptRecipientCollection, GetRecipients, }; var updateSelectMessage = new WaterfallStep[] { UpdateMessage, PromptUpdateMessage, AfterUpdateMessage, }; // Define the conversation flow using a waterfall model. AddDialog(new WaterfallDialog(Actions.Forward, forwardEmail) { TelemetryClient = telemetryClient }); AddDialog(new WaterfallDialog(Actions.Show, showEmail) { TelemetryClient = telemetryClient }); AddDialog(new WaterfallDialog(Actions.CollectRecipient, collectRecipients) { TelemetryClient = telemetryClient }); AddDialog(new WaterfallDialog(Actions.UpdateSelectMessage, updateSelectMessage) { TelemetryClient = telemetryClient }); AddDialog(new FindContactDialog(services, responseManager, emailStateAccessor, dialogStateAccessor, serviceManager, telemetryClient)); InitialDialogId = Actions.Forward; }
public async Task <DialogTurnResult> ConfirmNameList(WaterfallStepContext sc, CancellationToken cancellationToken = default(CancellationToken)) { try { var state = await Accessor.GetAsync(sc.Context); // got attendee name list already. if (state.AttendeesNameList.Any()) { return(await sc.NextAsync()); } // ask for attendee return(await sc.PromptAsync(Actions.Prompt, new PromptOptions { Prompt = ResponseManager.GetResponse(FindContactResponses.NoAttendees) }, cancellationToken)); } catch (Exception ex) { await HandleDialogExceptions(sc, ex); return(new DialogTurnResult(DialogTurnStatus.Cancelled, CommonUtil.DialogTurnResultCancelAllDialogs)); } }
public JoinEventDialog( BotSettings settings, BotServices services, ResponseManager responseManager, ConversationState conversationState, IServiceManager serviceManager, IBotTelemetryClient telemetryClient, MicrosoftAppCredentials appCredentials) : base(nameof(JoinEventDialog), settings, services, responseManager, conversationState, serviceManager, telemetryClient, appCredentials) { TelemetryClient = telemetryClient; var joinMeeting = new WaterfallStep[] { GetAuthToken, AfterGetAuthToken, CheckFocusedEvent, ConfirmNumber, AfterConfirmNumber }; var findEvent = new WaterfallStep[] { SearchEventsWithEntities, GetEvents, AddConflictFlag, ChooseEvent }; var getEvents = new WaterfallStep[] { GetEventsPrompt, AfterGetEventsPrompt, CheckValid, }; var chooseEvent = new WaterfallStep[] { ChooseEventPrompt, AfterChooseEvent }; AddDialog(new WaterfallDialog(Actions.ConnectToMeeting, joinMeeting) { TelemetryClient = telemetryClient }); AddDialog(new WaterfallDialog(Actions.GetEvents, getEvents) { TelemetryClient = telemetryClient }); AddDialog(new WaterfallDialog(Actions.FindEvent, findEvent) { TelemetryClient = telemetryClient }); AddDialog(new WaterfallDialog(Actions.ChooseEvent, chooseEvent) { TelemetryClient = telemetryClient }); // Set starting dialog for component InitialDialogId = Actions.ConnectToMeeting; }
public FindContactDialog( SkillConfigurationBase services, ResponseManager responseManager, IStatePropertyAccessor <CalendarSkillState> accessor, IServiceManager serviceManager, IBotTelemetryClient telemetryClient) : base(nameof(FindContactDialog), services, responseManager, accessor, serviceManager, telemetryClient) { TelemetryClient = telemetryClient; // entry, get the name list var confirmNameList = new WaterfallStep[] { ConfirmNameList, AfterConfirmNameList, }; // go through the name list, replace the confirmNameList // set state.CurrentAttendeeName var loopNameList = new WaterfallStep[] { LoopNameList, AfterLoopNameList }; // check on the attendee of state.CurrentAttendeeName. // called by loopNameList var confirmAttendee = new WaterfallStep[] { // call updateName to get the person state.ConfirmedPerson. // state.ConfirmedPerson should be set after this step ConfirmName, // check if the state.ConfirmedPerson // - null : failed to parse this name for multiple try. // - one email : check if this one is wanted // - multiple emails : call selectEmail ConfirmEmail, // if got no on last step, replace/restart this flow. AfterConfirmEmail }; // use the user name of state.CurrentAttendeeName or user input to find the persons. // and will call select person. // after all this done, state.ConfirmedPerson should be set. var updateName = new WaterfallStep[] { // check whether should the bot ask for attendee name. // if called by confirmAttendee then skip this step. // if called by itself when can not find the last input, it will ask back or end this one when multiple try. UpdateUserName, // check if email. add email direct into attendee and set state.ConfirmedPerson null. // if not, search for the attendee. // if got multiple persons, call selectPerson. use replace // if got no person, replace/restart this flow. AfterUpdateUserName, }; // select person, called bt updateName with replace. var selectPerson = new WaterfallStep[] { SelectPerson, AfterSelectPerson }; // select email. // called by ConfirmEmail var selectEmail = new WaterfallStep[] { SelectEmail, AfterSelectEmail }; AddDialog(new WaterfallDialog(Actions.ConfirmNameList, confirmNameList) { TelemetryClient = telemetryClient }); AddDialog(new WaterfallDialog(Actions.LoopNameList, loopNameList) { TelemetryClient = telemetryClient }); AddDialog(new WaterfallDialog(Actions.ConfirmAttendee, confirmAttendee) { TelemetryClient = telemetryClient }); AddDialog(new WaterfallDialog(Actions.UpdateName, updateName) { TelemetryClient = telemetryClient }); AddDialog(new WaterfallDialog(Actions.SelectPerson, selectPerson) { TelemetryClient = telemetryClient }); AddDialog(new WaterfallDialog(Actions.SelectEmail, selectEmail) { TelemetryClient = telemetryClient }); InitialDialogId = Actions.ConfirmNameList; }
public IActionResult AddPlot(PlotVm plot) { _service.AddPlot(plot); return(Ok(ResponseManager.GenerateResponse(null, (int)MessageType.Ok, plot))); }
public async Task <DialogTurnResult> UpdateUserName(WaterfallStepContext sc, CancellationToken cancellationToken = default(CancellationToken)) { try { var state = await Accessor.GetAsync(sc.Context); state.UnconfirmedPerson.Clear(); state.ConfirmedPerson = null; var options = (UpdateUserNameDialogOptions)sc.Options; // if it is confirm no, thenask user to give a new attendee if (options.Reason == UpdateUserNameDialogOptions.UpdateReason.ConfirmNo) { return(await sc.PromptAsync( Actions.Prompt, new PromptOptions { Prompt = ResponseManager.GetResponse(CreateEventResponses.NoAttendees) })); } var currentRecipientName = state.CurrentAttendeeName; // if not initialize ask user for attendee if (options.Reason != UpdateUserNameDialogOptions.UpdateReason.Initialize) { if (state.FirstRetryInFindContact) { state.FirstRetryInFindContact = false; return(await sc.PromptAsync( Actions.Prompt, new PromptOptions { Prompt = ResponseManager.GetResponse( FindContactResponses.UserNotFound, new StringDictionary() { { "UserName", currentRecipientName } }) })); } else { await sc.Context.SendActivityAsync(ResponseManager.GetResponse( FindContactResponses.UserNotFoundAgain, new StringDictionary() { { "source", state.EventSource == Models.EventSource.Microsoft ? "Outlook" : "Gmail" }, { "UserName", currentRecipientName } })); state.FirstRetryInFindContact = true; state.CurrentAttendeeName = string.Empty; return(await sc.EndDialogAsync()); } } return(await sc.NextAsync()); } catch (Exception ex) { await HandleDialogExceptions(sc, ex); return(new DialogTurnResult(DialogTurnStatus.Cancelled, CommonUtil.DialogTurnResultCancelAllDialogs)); } }
protected async Task <DialogTurnResult> AddTask(WaterfallStepContext sc, CancellationToken cancellationToken = default(CancellationToken)) { try { var state = await ToDoStateAccessor.GetAsync(sc.Context); if (state.AddDupTask) { state.ListType = state.ListType ?? ToDoStrings.ToDo; state.LastListType = state.ListType; var service = await InitListTypeIds(sc); var currentAllTasks = await service.GetTasksAsync(state.ListType); var duplicatedTaskIndex = currentAllTasks.FindIndex(t => t.Topic.Equals(state.TaskContent, StringComparison.InvariantCultureIgnoreCase)); //if (state.TaskContentPattern.ToLower().Contains("on my way home")) //{ var dateTime = new DateTime( DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, 17, 0, 0); await service.AddTaskAsync(state.ListType, state.TaskContent, dateTime); //} //else //{ // await service.AddTaskAsync(state.ListType, state.TaskContent); //} state.AllTasks = await service.GetTasksAsync(state.ListType); state.ShowTaskPageIndex = 0; var rangeCount = Math.Min(state.PageSize, state.AllTasks.Count); state.Tasks = state.AllTasks.GetRange(0, rangeCount); var toDoListCard = ToAdaptiveCardForTaskAddedFlow( sc.Context, state.Tasks, state.TaskContent, state.AllTasks.Count, state.ListType); toDoListCard.InputHint = InputHints.IgnoringInput; await sc.Context.SendActivityAsync(toDoListCard); return(await sc.NextAsync()); } else { await sc.Context.SendActivityAsync(ResponseManager.GetResponse(ToDoSharedResponses.ActionEnded)); return(await sc.EndDialogAsync(true)); } } catch (SkillException ex) { await HandleDialogExceptions(sc, ex); return(new DialogTurnResult(DialogTurnStatus.Cancelled, CommonUtil.DialogTurnResultCancelAllDialogs)); } catch (Exception ex) { await HandleDialogExceptions(sc, ex); return(new DialogTurnResult(DialogTurnStatus.Cancelled, CommonUtil.DialogTurnResultCancelAllDialogs)); } }
private async Task <PromptOptions> GenerateOptionsForName(WaterfallStepContext sc, List <CustomizedPerson> unionList, ITurnContext context, bool isSinglePage = true) { var state = await Accessor.GetAsync(context); var pageIndex = state.ShowAttendeesIndex; var pageSize = 3; var skip = pageSize * pageIndex; var currentRecipientName = state.CurrentAttendeeName; // Go back to the last page when reaching the end. if (skip >= unionList.Count && pageIndex > 0) { state.ShowAttendeesIndex--; pageIndex = state.ShowAttendeesIndex; skip = pageSize * pageIndex; await sc.Context.SendActivityAsync(ResponseManager.GetResponse(FindContactResponses.AlreadyLastPage)); } var options = new PromptOptions { Choices = new List <Choice>(), Prompt = ResponseManager.GetResponse(FindContactResponses.ConfirmMultipleContactNameSinglePage, new StringDictionary() { { "UserName", currentRecipientName } }) }; if (!isSinglePage) { options.Prompt = ResponseManager.GetResponse(FindContactResponses.ConfirmMultipleContactNameMultiPage, new StringDictionary() { { "UserName", currentRecipientName } }); } for (var i = 0; i < unionList.Count; i++) { var user = unionList[i]; var choice = new Choice() { Value = $"**{user.DisplayName}**", Synonyms = new List <string> { (options.Choices.Count + 1).ToString(), user.DisplayName, user.DisplayName.ToLower() }, }; var userName = user.UserPrincipalName?.Split("@").FirstOrDefault() ?? user.UserPrincipalName; if (!string.IsNullOrEmpty(userName)) { choice.Synonyms.Add(userName); choice.Synonyms.Add(userName.ToLower()); } if (skip <= 0) { if (options.Choices.Count >= pageSize) { options.Prompt.Speak = SpeechUtility.ListToSpeechReadyString(options, ReadPreference.Chronological, ConfigData.GetInstance().MaxReadSize); options.Prompt.Text = GetSelectPromptString(options, true); options.RetryPrompt = ResponseManager.GetResponse(CalendarSharedResponses.DidntUnderstandMessage); return(options); } options.Choices.Add(choice); } else { skip--; } } options.Prompt.Speak = SpeechUtility.ListToSpeechReadyString(options, ReadPreference.Chronological, ConfigData.GetInstance().MaxReadSize); options.Prompt.Text = GetSelectPromptString(options, true); options.RetryPrompt = ResponseManager.GetResponse(CalendarSharedResponses.DidntUnderstandMessage); return(options); }
public AddToDoItemDialog( BotSettings settings, BotServices services, ResponseManager responseManager, ConversationState conversationState, UserState userState, IServiceManager serviceManager, IBotTelemetryClient telemetryClient, MicrosoftAppCredentials appCredentials, IHttpContextAccessor httpContext) : base(nameof(AddToDoItemDialog), settings, services, responseManager, conversationState, userState, serviceManager, telemetryClient, appCredentials, httpContext) { TelemetryClient = telemetryClient; var addTask = new WaterfallStep[] { GetAuthToken, AfterGetAuthToken, ClearContext, DoAddTask, }; var doAddTask = new WaterfallStep[] { CollectTaskContent, CollectSwitchListTypeConfirmation, CollectAddDupTaskConfirmation, AddTask, ContinueAddTask, }; var collectTaskContent = new WaterfallStep[] { AskTaskContent, AfterAskTaskContent, }; var collectSwitchListTypeConfirmation = new WaterfallStep[] { AskSwitchListTypeConfirmation, AfterAskSwitchListTypeConfirmation, }; var collectAddDupTaskConfirmation = new WaterfallStep[] { AskAddDupTaskConfirmation, AfterAskAddDupTaskConfirmation, }; var continueAddTask = new WaterfallStep[] { AskContinueAddTask, AfterAskContinueAddTask, }; // Define the conversation flow using a waterfall model. AddDialog(new WaterfallDialog(Actions.DoAddTask, doAddTask) { TelemetryClient = telemetryClient }); AddDialog(new WaterfallDialog(Actions.AddTask, addTask) { TelemetryClient = telemetryClient }); AddDialog(new WaterfallDialog(Actions.CollectTaskContent, collectTaskContent) { TelemetryClient = telemetryClient }); AddDialog(new WaterfallDialog(Actions.CollectSwitchListTypeConfirmation, collectSwitchListTypeConfirmation) { TelemetryClient = telemetryClient }); AddDialog(new WaterfallDialog(Actions.CollectAddDupTaskConfirmation, collectAddDupTaskConfirmation) { TelemetryClient = telemetryClient }); AddDialog(new WaterfallDialog(Actions.ContinueAddTask, continueAddTask) { TelemetryClient = telemetryClient }); // Set starting dialog for component InitialDialogId = Actions.AddTask; }
protected async Task <DialogTurnResult> PromptToReshow(WaterfallStepContext sc, CancellationToken cancellationToken = default(CancellationToken)) { try { return(await sc.PromptAsync(Actions.Prompt, new PromptOptions { Prompt = ResponseManager.GetResponse(ShowEmailResponses.ReadOutMorePrompt) })); } catch (Exception ex) { await HandleDialogExceptions(sc, ex); return(new DialogTurnResult(DialogTurnStatus.Cancelled, CommonUtil.DialogTurnResultCancelAllDialogs)); } }
private async Task <DialogTurnResult> ShowEventsSummary(WaterfallStepContext sc, CancellationToken cancellationToken = default(CancellationToken)) { try { var tokenResponse = sc.Result as TokenResponse; var state = await Accessor.GetAsync(sc.Context); var options = sc.Options as ShowMeetingsDialogOptions; if (state.SummaryEvents == null) { // this will lead to error when test if (string.IsNullOrEmpty(state.APIToken)) { state.Clear(); return(await sc.EndDialogAsync(true)); } var calendarService = ServiceManager.InitCalendarService(state.APIToken, state.EventSource); state.SummaryEvents = await GetMeetingToJoin(sc); } if (state.SummaryEvents.Count == 0) { await sc.Context.SendActivityAsync(ResponseManager.GetResponse(JoinEventResponses.MeetingNotFound)); state.Clear(); return(await sc.EndDialogAsync(true)); } else if (state.SummaryEvents.Count == 1) { state.ConfirmedMeeting.Add(state.SummaryEvents.First()); return(await sc.ReplaceDialogAsync(Actions.ConfirmNumber, sc.Options)); } // Multiple events var firstEvent = GetCurrentPageMeetings(state.SummaryEvents, state).First(); var responseParams = new StringDictionary() { { "EventName1", firstEvent.Title }, { "EventTime1", SpeakHelper.ToSpeechMeetingTime(TimeConverter.ConvertUtcToUserTime(firstEvent.StartTime, state.GetUserTimeZone()), firstEvent.IsAllDay == true) }, { "Participants1", DisplayHelper.ToDisplayParticipantsStringSummary(firstEvent.Attendees, 1) } }; var reply = await GetGeneralMeetingListResponseAsync(sc, CalendarCommonStrings.MeetingsToJoin, GetCurrentPageMeetings(state.SummaryEvents, state), JoinEventResponses.SelectMeeting, responseParams); return(await sc.PromptAsync(Actions.Prompt, new PromptOptions() { Prompt = reply })); } catch (SkillException ex) { await HandleDialogExceptions(sc, ex); return(new DialogTurnResult(DialogTurnStatus.Cancelled, CommonUtil.DialogTurnResultCancelAllDialogs)); } catch (Exception ex) { await HandleDialogExceptions(sc, ex); return(new DialogTurnResult(DialogTurnStatus.Cancelled, CommonUtil.DialogTurnResultCancelAllDialogs)); } }
protected async Task <DialogTurnResult> HandleMore(WaterfallStepContext sc, CancellationToken cancellationToken = default(CancellationToken)) { try { var state = await EmailStateAccessor.GetAsync(sc.Context); var luisResult = sc.Context.TurnState.Get <EmailLuis>(StateProperties.EmailLuisResult); var topIntent = luisResult?.TopIntent().intent; var generalIntent = sc.Context.TurnState.Get <General>(StateProperties.GeneralLuisResult); var topGeneralIntent = generalIntent?.TopIntent().intent; if (topIntent == null) { return(await sc.EndDialogAsync(true)); } sc.Context.Activity.Properties.TryGetValue("OriginText", out var content); var userInput = content != null?content.ToString() : sc.Context.Activity.Text; await DigestFocusEmailAsync(sc); var skillOptions = (EmailSkillDialogOptions)sc.Options; skillOptions.SubFlowMode = true; if (topIntent == EmailLuis.Intent.Delete) { return(await sc.BeginDialogAsync(Actions.Delete, skillOptions)); } else if (topIntent == EmailLuis.Intent.Forward) { return(await sc.BeginDialogAsync(Actions.Forward, skillOptions)); } else if (topIntent == EmailLuis.Intent.Reply) { return(await sc.BeginDialogAsync(Actions.Reply, skillOptions)); } else if (IsReadMoreIntent(topGeneralIntent, userInput) || (topIntent == EmailLuis.Intent.ShowNext || topIntent == EmailLuis.Intent.ShowPrevious || topGeneralIntent == General.Intent.ShowPrevious || topGeneralIntent == General.Intent.ShowNext)) { return(await sc.ReplaceDialogAsync(Actions.Display, skillOptions)); } else { await DigestEmailLuisResult(sc, true); await SearchEmailsFromList(sc, cancellationToken); if (state.MessageList.Count > 0) { return(await sc.ReplaceDialogAsync(Actions.DisplayFiltered, skillOptions)); } await sc.Context.SendActivityAsync(ResponseManager.GetResponse(EmailSharedResponses.DidntUnderstandMessage)); return(await sc.EndDialogAsync(true)); } } catch (Exception ex) { await HandleDialogExceptions(sc, ex); return(new DialogTurnResult(DialogTurnStatus.Cancelled, CommonUtil.DialogTurnResultCancelAllDialogs)); } }
private async Task <DialogTurnResult> AfterSelectEvent(WaterfallStepContext sc, CancellationToken cancellationToken = default(CancellationToken)) { try { var state = await Accessor.GetAsync(sc.Context); var luisResult = state.LuisResult; var topIntent = luisResult?.TopIntent().intent; var generalLuisResult = state.GeneralLuisResult; var generalTopIntent = generalLuisResult?.TopIntent().intent; if (topIntent == null) { state.Clear(); return(await sc.CancelAllDialogsAsync()); } if (generalTopIntent == General.Intent.ShowNext && state.SummaryEvents != null) { if ((state.ShowEventIndex + 1) * state.PageSize < state.SummaryEvents.Count) { state.ShowEventIndex++; } else { await sc.Context.SendActivityAsync(ResponseManager.GetResponse(SummaryResponses.CalendarNoMoreEvent)); } return(await sc.ReplaceDialogAsync(Actions.ConnectToMeeting, sc.Options)); } else if (generalTopIntent == General.Intent.ShowPrevious && state.SummaryEvents != null) { if (state.ShowEventIndex > 0) { state.ShowEventIndex--; } else { await sc.Context.SendActivityAsync(ResponseManager.GetResponse(SummaryResponses.CalendarNoPreviousEvent)); } return(await sc.ReplaceDialogAsync(Actions.ConnectToMeeting, sc.Options)); } sc.Context.Activity.Properties.TryGetValue("OriginText", out var content); var userInput = content != null?content.ToString() : sc.Context.Activity.Text; var promptRecognizerResult = ConfirmRecognizerHelper.ConfirmYesOrNo(userInput, sc.Context.Activity.Locale); if (promptRecognizerResult.Succeeded && promptRecognizerResult.Value == false) { state.Clear(); return(await sc.CancelAllDialogsAsync()); } else if (promptRecognizerResult.Succeeded && promptRecognizerResult.Value == true) { var currentList = GetCurrentPageMeetings(state.SummaryEvents, state); state.ConfirmedMeeting.Add(currentList.First()); return(await sc.ReplaceDialogAsync(Actions.ConfirmNumber, sc.Options)); } else if (state.SummaryEvents.Count == 1) { state.Clear(); return(await sc.CancelAllDialogsAsync()); } if (state.SummaryEvents.Count > 1) { var filteredMeetingList = new List <EventModel>(); var showMeetingReason = ShowMeetingReason.FirstShowOverview; string filterKeyWord = null; // filter meetings with number if (luisResult.Entities.ordinal != null) { var value = luisResult.Entities.ordinal[0]; var num = int.Parse(value.ToString()); var currentList = GetCurrentPageMeetings(state.SummaryEvents, state); if (num > 0 && num <= currentList.Count) { filteredMeetingList.Add(currentList[num - 1]); } } if (filteredMeetingList.Count <= 0 && generalLuisResult.Entities.number != null && (luisResult.Entities.ordinal == null || luisResult.Entities.ordinal.Length == 0)) { var value = generalLuisResult.Entities.number[0]; var num = int.Parse(value.ToString()); var currentList = GetCurrentPageMeetings(state.SummaryEvents, state); if (num > 0 && num <= currentList.Count) { filteredMeetingList.Add(currentList[num - 1]); } } // filter meetings with start time var timeResult = RecognizeDateTime(userInput, sc.Context.Activity.Locale ?? English, false); if (filteredMeetingList.Count <= 0 && timeResult != null) { foreach (var result in timeResult) { var dateTimeConvertTypeString = result.Timex; var dateTimeConvertType = new TimexProperty(dateTimeConvertTypeString); if (result.Value != null || (dateTimeConvertType.Types.Contains(Constants.TimexTypes.Time) || dateTimeConvertType.Types.Contains(Constants.TimexTypes.DateTime))) { var dateTime = DateTime.Parse(result.Value); if (dateTime != null) { var utcStartTime = TimeZoneInfo.ConvertTimeToUtc(dateTime, state.GetUserTimeZone()); foreach (var meeting in GetCurrentPageMeetings(state.SummaryEvents, state)) { if (meeting.StartTime.TimeOfDay == utcStartTime.TimeOfDay) { showMeetingReason = ShowMeetingReason.ShowFilteredByTimeMeetings; filterKeyWord = string.Format("H:mm", dateTime); filteredMeetingList.Add(meeting); } } } } } } // filter meetings with subject if (filteredMeetingList.Count <= 0) { var subject = userInput; if (luisResult.Entities.Subject != null) { subject = GetSubjectFromEntity(luisResult.Entities); } foreach (var meeting in GetCurrentPageMeetings(state.SummaryEvents, state)) { if (meeting.Title.ToLower().Contains(subject.ToLower())) { showMeetingReason = ShowMeetingReason.ShowFilteredByTitleMeetings; filterKeyWord = subject; filteredMeetingList.Add(meeting); } } } // filter meetings with contact name if (filteredMeetingList.Count <= 0) { var contactNameList = new List <string>() { userInput }; if (luisResult.Entities.personName != null) { contactNameList = GetAttendeesFromEntity(luisResult.Entities, userInput); } foreach (var meeting in GetCurrentPageMeetings(state.SummaryEvents, state)) { var containsAllContacts = true; foreach (var contactName in contactNameList) { if (!meeting.ContainsAttendee(contactName)) { containsAllContacts = false; break; } } if (containsAllContacts) { showMeetingReason = ShowMeetingReason.ShowFilteredByParticipantNameMeetings; filterKeyWord = string.Join(", ", contactNameList); filteredMeetingList.Add(meeting); } } } if (filteredMeetingList.Count == 1) { state.ReadOutEvents = filteredMeetingList; return(await sc.BeginDialogAsync(Actions.ConfirmNumber, sc.Options)); } else if (filteredMeetingList.Count > 1) { state.SummaryEvents = filteredMeetingList; state.FilterMeetingKeyWord = filterKeyWord; return(await sc.ReplaceDialogAsync(Actions.ConnectToMeeting, new ShowMeetingsDialogOptions(showMeetingReason, sc.Options))); } } if (state.ConfirmedMeeting != null && state.ConfirmedMeeting.Count > 0) { return(await sc.ReplaceDialogAsync(Actions.ConfirmNumber, sc.Options)); } else { state.Clear(); return(await sc.CancelAllDialogsAsync()); } } catch (Exception ex) { await HandleDialogExceptions(sc, ex); return(new DialogTurnResult(DialogTurnStatus.Cancelled, CommonUtil.DialogTurnResultCancelAllDialogs)); } }
/// <summary> /// Look up parking points of interest, render cards, and ask user which to route to. /// </summary> /// <param name="sc">Step Context.</param> /// <param name="cancellationToken">Cancellation Token.</param> /// <returns>Dialog Turn Result.</returns> protected async Task <DialogTurnResult> GetParkingInterestPoints(WaterfallStepContext sc, CancellationToken cancellationToken = default(CancellationToken)) { try { var state = await Accessor.GetAsync(sc.Context); var mapsService = ServiceManager.InitMapsService(Settings, sc.Context.Activity.Locale); var addressMapsService = ServiceManager.InitAddressMapsService(Settings, sc.Context.Activity.Locale); var pointOfInterestList = new List <PointOfInterestModel>(); var cards = new List <Card>(); if (!string.IsNullOrEmpty(state.Address)) { // Get first POI matched with address, if there are multiple this could be expanded to confirm which address to use var pointOfInterestAddressList = await addressMapsService.GetPointOfInterestListByQueryAsync(double.NaN, double.NaN, state.Address); if (pointOfInterestAddressList.Any()) { var pointOfInterest = pointOfInterestAddressList[0]; pointOfInterestList = await mapsService.GetPointOfInterestListByParkingCategoryAsync(pointOfInterest.Geolocation.Latitude, pointOfInterest.Geolocation.Longitude); cards = await GetPointOfInterestLocationCards(sc, pointOfInterestList); } else { // Find parking lot near address pointOfInterestList = await mapsService.GetPointOfInterestListByParkingCategoryAsync(state.CurrentCoordinates.Latitude, state.CurrentCoordinates.Longitude); cards = await GetPointOfInterestLocationCards(sc, pointOfInterestList); } } else { // No entities identified, find nearby parking lots pointOfInterestList = await mapsService.GetPointOfInterestListByParkingCategoryAsync(state.CurrentCoordinates.Latitude, state.CurrentCoordinates.Longitude); cards = await GetPointOfInterestLocationCards(sc, pointOfInterestList); } if (cards.Count == 0) { var replyMessage = ResponseManager.GetResponse(POISharedResponses.NoLocationsFound); await sc.Context.SendActivityAsync(replyMessage); return(await sc.EndDialogAsync()); } else if (cards.Count == 1) { pointOfInterestList[0].SubmitText = GetConfirmPromptTrue(); var options = new PromptOptions { Prompt = ResponseManager.GetCardResponse(POISharedResponses.PromptToGetRoute, cards) }; // Workaround. In teams, HeroCard will be used for prompt 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.ConfirmPrompt, options)); } else { var options = GetPointOfInterestPrompt(POISharedResponses.MultipleLocationsFound, pointOfInterestList, cards); // Workaround. In teams, HeroCard will be used for prompt 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.SelectPointOfInterestPrompt, options)); } } catch (Exception ex) { await HandleDialogExceptions(sc, ex); return(new DialogTurnResult(DialogTurnStatus.Cancelled, CommonUtil.DialogTurnResultCancelAllDialogs)); } }
public async Task <DialogTurnResult> AfterGetRecreateInfo(WaterfallStepContext sc, CancellationToken cancellationToken = default(CancellationToken)) { try { var state = await Accessor.GetAsync(sc.Context, cancellationToken : cancellationToken); if (sc.Result != null) { var recreateState = sc.Result as RecreateEventState?; switch (recreateState.Value) { case RecreateEventState.Cancel: await sc.Context.SendActivityAsync(ResponseManager.GetResponse(CalendarSharedResponses.ActionEnded), cancellationToken); state.Clear(); return(await sc.EndDialogAsync(true, cancellationToken)); case RecreateEventState.Time: state.ClearTimes(); return(await sc.ReplaceDialogAsync(Actions.CreateEvent, options : sc.Options, cancellationToken : cancellationToken)); case RecreateEventState.Duration: state.ClearTimesExceptStartTime(); return(await sc.ReplaceDialogAsync(Actions.CreateEvent, options : sc.Options, cancellationToken : cancellationToken)); case RecreateEventState.Location: state.ClearLocation(); return(await sc.ReplaceDialogAsync(Actions.CreateEvent, options : sc.Options, cancellationToken : cancellationToken)); case RecreateEventState.Participants: state.ClearParticipants(); return(await sc.ReplaceDialogAsync(Actions.CreateEvent, options : sc.Options, cancellationToken : cancellationToken)); case RecreateEventState.Subject: state.ClearSubject(); return(await sc.ReplaceDialogAsync(Actions.CreateEvent, options : sc.Options, cancellationToken : cancellationToken)); case RecreateEventState.Content: state.ClearContent(); return(await sc.ReplaceDialogAsync(Actions.CreateEvent, options : sc.Options, cancellationToken : cancellationToken)); default: // should not go to this part. place an error handling for save. await HandleDialogExceptions(sc, new Exception("Get unexpect state in recreate.")); return(new DialogTurnResult(DialogTurnStatus.Cancelled, CommonUtil.DialogTurnResultCancelAllDialogs)); } } else { // should not go to this part. place an error handling for save. await HandleDialogExceptions(sc, new Exception("Get unexpect result in recreate.")); return(new DialogTurnResult(DialogTurnStatus.Cancelled, CommonUtil.DialogTurnResultCancelAllDialogs)); } } catch (Exception ex) { await HandleDialogExceptions(sc, ex); return(new DialogTurnResult(DialogTurnStatus.Cancelled, CommonUtil.DialogTurnResultCancelAllDialogs)); } }