Created
August 11, 2020 06:30
-
-
Save jsiegmund/831d5337b1a438133991070daba8a27e to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class HourBookingDialog : AuthenticatedDialog | |
{ | |
private readonly string _propNameModel = "HourBookingModel"; | |
private readonly string _propNameCustomers = "Customers"; | |
private readonly string _propNameProjects = "Projects"; | |
public HourBookingDialog(IConfiguration configuration) : base (nameof(HourBookingDialog), configuration["ConnectionName"]) | |
{ | |
var waterFallSteps = new WaterfallStep[] | |
{ | |
PromptStep, | |
LoginStep, | |
CustomerStep, | |
ProjectStep, | |
HourTypeStep, | |
AmountStep, | |
DateStep, | |
ConfirmStep, | |
BookingStep | |
}; | |
AddDialog(new OAuthPrompt( | |
nameof(OAuthPrompt), | |
new OAuthPromptSettings | |
{ | |
ConnectionName = ConnectionName, | |
Text = "Log into Exact Online. Please paste back the verification number in the chat.", | |
Title = "Login", | |
Timeout = 300000, // User has 5 minutes to login | |
})); | |
AddDialog(new WaterfallDialog(nameof(WaterfallDialog), waterFallSteps)); | |
AddDialog(new GuidPrompt(nameof(CustomerStep))); | |
AddDialog(new GuidPrompt(nameof(ProjectStep))); | |
AddDialog(new GuidPrompt(nameof(HourTypeStep))); | |
AddDialog(new NumberPrompt<long>(nameof(AmountStep))); | |
AddDialog(new DateTimePrompt(nameof(DateStep))); | |
AddDialog(new ConfirmPrompt(nameof(ConfirmStep))); | |
InitialDialogId = nameof(WaterfallDialog); | |
} | |
private async Task<DialogTurnResult> PromptStep(WaterfallStepContext stepContext, CancellationToken cancellationToken) | |
{ | |
var botAdapter = (BotFrameworkAdapter)stepContext.Context.Adapter; | |
await botAdapter.SignOutUserAsync(stepContext.Context, ConnectionName, null, cancellationToken); | |
return await stepContext.BeginDialogAsync(nameof(OAuthPrompt), null, cancellationToken); | |
} | |
private async Task<DialogTurnResult> LoginStep(WaterfallStepContext stepContext, CancellationToken cancellationToken) | |
{ | |
// Get the token from the previous step. Note that we could also have gotten the | |
// token directly from the prompt itself. There is an example of this in the next method. | |
var tokenResponse = (TokenResponse)stepContext.Result; | |
if (tokenResponse != null) | |
{ | |
stepContext.Values["AccessToken"] = tokenResponse; | |
await stepContext.Context.SendActivityAsync(MessageFactory.Text("You are now logged in."), cancellationToken); | |
return await stepContext.ContinueDialogAsync(); | |
} | |
else | |
{ | |
return await stepContext.ReplaceDialogAsync(nameof(OAuthPrompt), null, cancellationToken); | |
} | |
} | |
private AdaptiveCard RegisterCustomerDialog(IEnumerable<HourBookingCustomerModel> customers) | |
{ | |
var card = new AdaptiveCard(new AdaptiveSchemaVersion(1, 2)) | |
{ | |
Body = new List<AdaptiveElement> | |
{ | |
new AdaptiveTextBlock("Please select a customer"), | |
} | |
}; | |
card.Body.Add(new AdaptiveActionSet() | |
{ | |
Actions = customers.Select(c => new AdaptiveSubmitAction() { Data = c.Id, Title = c.Name }).ToList<AdaptiveAction>() | |
}); | |
AddDialog(new AdaptiveCardPrompt(nameof(CustomerStep), new AdaptiveCardPromptSettings() | |
{ | |
Card = new Attachment | |
{ | |
ContentType = AdaptiveCard.ContentType, | |
Content = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(card)) | |
}, | |
PromptId = "customerPrompt" | |
})); | |
return card; | |
} | |
private async Task<DialogTurnResult> CustomerStep(WaterfallStepContext stepContext, CancellationToken cancellationToken) | |
{ | |
var timeRegistrationRepository = new TimeRegistrationRepository(() => GetExactAccessToken(stepContext.Context)); | |
var accounts = timeRegistrationRepository.GetRecentAccounts(); | |
var customers = accounts.Select(a => new HourBookingCustomerModel() { Id = a.AccountId, Name = a.AccountName }); | |
if (customers.Count() == 0) | |
{ | |
await stepContext.Context.SendActivityAsync(MessageFactory.Text("I checked Exact but didn't find any recent hours booked. At the moment I'm limited to booking hours on customers you've previously used.")); | |
return await stepContext.EndDialogAsync(); | |
} | |
else if (customers.Count() == 1) | |
{ | |
var customerId = customers.First().Id; | |
stepContext.Values[nameof(HourBookingModel)] = new HourBookingModel() { CustomerId = customerId }; | |
return await stepContext.ContinueDialogAsync(); | |
} | |
else | |
{ | |
var card = RegisterCustomerDialog(customers); | |
return await stepContext.PromptAsync( | |
nameof(CustomerStep), | |
new PromptOptions() | |
{ | |
Prompt = (Activity)MessageFactory.Attachment(new Attachment | |
{ | |
ContentType = AdaptiveCard.ContentType, | |
Content = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(card)) | |
}) | |
}, | |
cancellationToken | |
); | |
} | |
} | |
private AdaptiveCard RegisterProjectDialog(ITurnContext turnContext, Guid customerId) | |
{ | |
var timeRegistrationRepository = new TimeRegistrationRepository(() => GetExactAccessToken(turnContext)); | |
var projects = timeRegistrationRepository.GetRecentProjectsByAccountId(customerId); | |
var card = new AdaptiveCard(new AdaptiveSchemaVersion(1, 2)) | |
{ | |
Body = new List<AdaptiveElement> | |
{ | |
new AdaptiveTextBlock("Please select a project"), | |
} | |
}; | |
card.Body.Add(new AdaptiveActionSet() | |
{ | |
Actions = projects.Select(c => new AdaptiveSubmitAction() { Data = c.ProjectId, Title = c.ProjectDescription }).ToList<AdaptiveAction>() | |
}); | |
AddDialog(new AdaptiveCardPrompt(nameof(ProjectStep), new AdaptiveCardPromptSettings() | |
{ | |
Card = new Attachment | |
{ | |
ContentType = AdaptiveCard.ContentType, | |
Content = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(card)) | |
}, | |
PromptId = "projectPrompt" | |
} | |
)); | |
return card; | |
} | |
private async Task<DialogTurnResult> ProjectStep(WaterfallStepContext stepContext, CancellationToken cancellationToken) | |
{ | |
// Save the value of the previous step (Customer Selection) | |
if (stepContext.Result is Guid) | |
{ | |
var customerId = (Guid)stepContext.Result; | |
stepContext.Values[nameof(HourBookingModel)] = new HourBookingModel() { CustomerId = customerId }; | |
} | |
var card = RegisterProjectDialog(stepContext.Context, customerId); | |
return await stepContext.PromptAsync( | |
nameof(ProjectStep), | |
new PromptOptions() | |
{ | |
Prompt = (Activity)MessageFactory.Attachment(new Attachment | |
{ | |
ContentType = AdaptiveCard.ContentType, | |
Content = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(card)) | |
}) | |
}, | |
cancellationToken | |
); | |
} | |
private AdaptiveCard RegisterHourTypeDialog(ITurnContext turnContext) | |
{ | |
var timeRegistrationRepository = new TimeRegistrationRepository(() => GetExactAccessToken(turnContext)); | |
var projects = timeRegistrationRepository.GetRecentHourCostTypes(); | |
var card = new AdaptiveCard(new AdaptiveSchemaVersion(1, 2)) | |
{ | |
Body = new List<AdaptiveElement> | |
{ | |
new AdaptiveTextBlock("Please select a cost (hour) type."), | |
} | |
}; | |
card.Body.Add(new AdaptiveActionSet() | |
{ | |
Actions = projects.Select(c => new AdaptiveSubmitAction() { Data = c.ItemId, Title = c.ItemDescription}).ToList<AdaptiveAction>() | |
}); | |
AddDialog(new AdaptiveCardPrompt(nameof(HourTypeStep), new AdaptiveCardPromptSettings() | |
{ | |
Card = new Attachment | |
{ | |
ContentType = AdaptiveCard.ContentType, | |
Content = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(card)) | |
}, | |
PromptId = "hourPrompt" | |
})); | |
return card; | |
} | |
private async Task<DialogTurnResult> HourTypeStep(WaterfallStepContext stepContext, CancellationToken cancellationToken) | |
{ | |
// save the value of the previous step (Project Selection) | |
if (stepContext.Result is Guid) | |
{ | |
var model = (HourBookingModel)stepContext.Values[nameof(HourBookingModel)]; | |
var projectId = (Guid)stepContext.Result; | |
model.ProjectId = projectId; | |
} | |
var card = RegisterHourTypeDialog(stepContext.Context); | |
return await stepContext.PromptAsync( | |
nameof(ProjectStep), | |
new PromptOptions() | |
{ | |
Prompt = (Activity)MessageFactory.Attachment(new Attachment | |
{ | |
ContentType = AdaptiveCard.ContentType, | |
Content = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(card)) | |
}) | |
}, | |
cancellationToken | |
); | |
} | |
private async Task<DialogTurnResult> AmountStep(WaterfallStepContext stepContext, CancellationToken cancellationToken) | |
{ | |
if (stepContext.Result is Guid) | |
{ | |
var model = (HourBookingModel)stepContext.Values[nameof(HourBookingModel)]; | |
var hourTypeId = (Guid)stepContext.Result; | |
model.HourTypeId = hourTypeId; | |
} | |
return await stepContext.PromptAsync(nameof(AmountStep), new PromptOptions { Prompt = MessageFactory.Text($"Enter the amount of hours") }, cancellationToken); | |
} | |
private async Task<DialogTurnResult> DateStep(WaterfallStepContext stepContext, CancellationToken cancellationToken) | |
{ | |
var model = (HourBookingModel)stepContext.Values[nameof(HourBookingModel)]; | |
var amount = (long)stepContext.Result; | |
model.NumberOfHours = amount; | |
return await stepContext.PromptAsync(nameof(DateStep), new PromptOptions { Prompt = MessageFactory.Text($"Enter the date on which to book") }, cancellationToken); | |
} | |
private async Task<DialogTurnResult> ConfirmStep(WaterfallStepContext stepContext, CancellationToken cancellationToken) | |
{ | |
var model = (HourBookingModel)stepContext.Values[nameof(HourBookingModel)]; | |
var dateResolution = (IList<DateTimeResolution>)stepContext.Result; | |
model.Date = DateTime.Parse(dateResolution.FirstOrDefault().Value); | |
return await stepContext.PromptAsync(nameof(ConfirmStep), new PromptOptions { Prompt = MessageFactory.Text($"Please confirm that you want to book {model.NumberOfHours} hours.") }, cancellationToken); | |
} | |
private async Task<DialogTurnResult> BookingStep(WaterfallStepContext stepContext, CancellationToken cancellationToken) | |
{ | |
var confirmed = (bool)stepContext.Result; | |
if (confirmed) | |
{ | |
var hourBookingModel = stepContext.GetValue<HourBookingModel>(nameof(HourBookingModel)); | |
var employeeRepository = new EmployeeRepository(() => GetExactAccessToken(stepContext.Context)); | |
var timeRegistrationRepository = new TimeRegistrationRepository(() => GetExactAccessToken(stepContext.Context)); | |
var employeeId = employeeRepository.GetEmployeeID(); | |
var date = hourBookingModel.Date.HasValue ? hourBookingModel.Date.Value : DateTime.Now; | |
var amount = hourBookingModel.NumberOfHours.HasValue ? hourBookingModel.NumberOfHours.Value : 8; | |
// do the actual booking stuff here | |
timeRegistrationRepository.BookHours(employeeId, hourBookingModel.CustomerId, hourBookingModel.HourTypeId, hourBookingModel.ProjectId, date, amount); | |
await stepContext.Context.SendActivityAsync(MessageFactory.Text("Great, I've booked your hours into Exact Online!")); | |
} | |
else | |
{ | |
await stepContext.Context.SendActivityAsync(MessageFactory.Text("That's ok! Feel free to ask me again to book your hours.")); | |
} | |
return await stepContext.EndDialogAsync(null, cancellationToken); | |
} | |
private string GetExactAccessToken(ITurnContext turnContext) | |
{ | |
var botAdapter = (BotFrameworkAdapter)turnContext.Adapter; | |
var getTokenTask = botAdapter.GetUserTokenAsync(turnContext, ConnectionName, null); | |
getTokenTask.Wait(); | |
return getTokenTask.Result?.Token; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment