Last active
May 8, 2018 23:56
-
-
Save vcardins/23a9f363ef1eeb7a3414790193148881 to your computer and use it in GitHub Desktop.
Multiple contexts BOT sample
This file contains hidden or 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
using System.Collections.Generic; | |
namespace MyBot.Api.Bot | |
{ | |
public class GlobalState : Dictionary<string, object> | |
{ | |
private const string UpdateProfileKey = "UpdateProfile"; | |
private const string CreateProjectKey = "CreateProject"; | |
public GlobalState() | |
{ | |
this[UpdateProfileKey] = new UpdateProfileState(); | |
this[CreateProjectKey] = new CreateProjectState(); | |
} | |
public UpdateProfileState UpdateProfile | |
{ | |
get { return (UpdateProfileState)this[UpdateProfileKey]; } | |
set { this[UpdateProfileKey] = value; } | |
} | |
public CreateProjectState CreateProject | |
{ | |
get { return (CreateProjectState)this[CreateProjectKey]; } | |
set { this[CreateProjectKey] = value; } | |
} | |
public string PhoneNumber | |
{ | |
get { return (string)this[PhoneNumberKey]; } | |
set { this[PhoneNumberKey] = value; } | |
} | |
} | |
} |
This file contains hidden or 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
using System; | |
using System.Collections.Generic; | |
using System.IO; | |
using System.Threading.Tasks; | |
using MyBot.Core.Interfaces; | |
using Microsoft.Bot; | |
using Microsoft.Bot.Builder; | |
using Microsoft.Bot.Builder.Core.Extensions; | |
using Microsoft.Bot.Builder.Dialogs; | |
using Microsoft.Bot.Builder.Prompts; | |
using Microsoft.Bot.Builder.Prompts.Choices; | |
using Microsoft.Bot.Schema; | |
using Microsoft.Recognizers.Text; | |
using Newtonsoft.Json; | |
using TextPrompt = Microsoft.Bot.Builder.Dialogs.TextPrompt; | |
using ChoicePrompt = Microsoft.Bot.Builder.Dialogs.ChoicePrompt; | |
namespace MyBot.Api.Bot | |
{ | |
public static class PromptStep | |
{ | |
public const string GatherInfo = "gatherInfo"; | |
public const string CardPrompt = "cardPrompt"; | |
public const string CardSelector = "cardSelector"; | |
} | |
public static class MenuOptions | |
{ | |
public static KeyValuePair<string, string> CreateProject = new KeyValuePair<string, string>("1", "Create Project"); | |
public static KeyValuePair<string, string> UpdateProfile = new KeyValuePair<string, string>("2", "Update Profile"); | |
public static KeyValuePair<string, string> Help = new KeyValuePair<string, string>("3", "Help"); | |
} | |
public partial class MyBot : IBot | |
{ | |
private readonly DialogSet _dialogs; | |
private readonly DialogSet _updateProfileDialogs; | |
private readonly ChoicePromptOptions _cardOptions; | |
private GlobalState GlobalState; | |
private ITurnContext _globalContext; | |
private IUserProfileService _userProfileService; | |
private ILookupService _lookupService; | |
public MyBot( | |
IUserProfileService userProfileService, | |
ILookupService lookupService | |
) | |
{ | |
_lookupService = lookupService; | |
_userProfileService = userProfileService; | |
_dialogs = new DialogSet(); | |
// Choice prompt with list style to show card types | |
var cardPrompt = new ChoicePrompt(Culture.English) | |
{ | |
Style = ListStyle.SuggestedAction | |
}; | |
_cardOptions = GenerateOptions(); | |
// Register the card prompt | |
_dialogs.Add(PromptStep.CardPrompt, cardPrompt); | |
// Create a dialog waterfall for prompting the user | |
_dialogs.Add(PromptStep.CardSelector, new WaterfallStep[] { | |
ChoiceCardStepAsync, | |
ShowMenuOptions | |
}); | |
_updateProfileDialogs = new DialogSet(); | |
// Create prompt for name with string length validation | |
_dialogs.Add(UpdateProfilePromptStep.FirstNamePrompt, new TextPrompt(FirstNameValidator)); | |
_dialogs.Add(UpdateProfilePromptStep.LastNamePrompt, new TextPrompt(LastNameValidator)); | |
_dialogs.Add(UpdateProfilePromptStep.PhoneNumberPrompt, new TextPrompt(PhoneNumberValidator)); | |
// Create a dialog waterfall for prompting the user | |
_dialogs.Add(UpdateProfilePromptStep.GatherUserProfile, new WaterfallStep[] { | |
AskFirstNameStep, | |
AskLastNameStep, | |
AskPhoneNumberStep, | |
GatherUserProfileStep | |
}); | |
// TODO: Add dialogs for other options | |
} | |
public async Task OnTurn(ITurnContext context) | |
{ | |
_globalContext = context; | |
GlobalState = context.GetConversationState<GlobalState>(); | |
var dialogContext = _dialogs.CreateContext(context, GlobalState); | |
switch (context.Activity.Type) | |
{ | |
case ActivityTypes.ConversationUpdate: | |
var from = dialogContext.Context.Activity.From; | |
var user = await _userProfileService.GetUserByUsernameAsync(from.Id); | |
var userDisplayName = user != null ? user.DisplayName : from.Name; | |
var message = user != null | |
? "welcome back, hope you're doing well" | |
: "it seems you're new around here. Welcome!"; | |
await dialogContext.Context.SendActivity($"Hello {userDisplayName}, {message}. How can I help you today?"); | |
break; | |
case ActivityTypes.Message: | |
var activeDialog = dialogContext.ActiveDialog; | |
await dialogContext.Continue(); | |
//Console.WriteLine(activeDialog?.Id); | |
if (!context.Responded) | |
{ | |
await dialogContext.Begin(PromptStep.CardSelector); // UpdateProfilePromptStep.GatherUserProfile | |
} | |
break; | |
case ActivityTypes.DeleteUserData: | |
// Implement user deletion here | |
// If we handle user deletion, return a real message | |
break; | |
case ActivityTypes.ContactRelationUpdate: | |
// Handle add/remove from contact lists | |
// Activity.From + Activity.Action represent what happened | |
break; | |
case ActivityTypes.Typing: | |
// Handle knowing that the user is typing | |
break; | |
case ActivityTypes.Ping: | |
await context.SendActivity("Pong"); | |
break; | |
default: | |
break; | |
} | |
} | |
// Create our prompt's choices | |
private ChoicePromptOptions GenerateOptions() | |
{ | |
return new ChoicePromptOptions() | |
{ | |
Choices = new List<Choice>() | |
{ | |
new Choice() | |
{ | |
Value = MenuOptions.CreateProject.Value, | |
Synonyms = new List<string>() { MenuOptions.UpdateProfile.Key, MenuOptions.CreateProject.Value.ToLower() } | |
}, | |
new Choice() | |
{ | |
Value = MenuOptions.UpdateProfile.Value, | |
Synonyms = new List<string>() { MenuOptions.UpdateProfile.Key, MenuOptions.UpdateProfile.Value.ToLower() } | |
}, | |
new Choice() | |
{ | |
Value = MenuOptions.Help.Value, | |
Synonyms = new List<string>() { MenuOptions.Help.Key, MenuOptions.Help.Value.ToLower() } | |
}, | |
} | |
}; | |
} | |
private async Task ChoiceCardStepAsync(DialogContext dialogContext, object result, SkipStepFunction next) | |
{ | |
await dialogContext.Prompt(PromptStep.CardPrompt, "Which option would you like to choose?", _cardOptions); | |
} | |
private async Task ShowMenuOptions(DialogContext dialogContext, object result, SkipStepFunction next) | |
{ | |
var selectedCard = (result as ChoiceResult).Value.Value; | |
var activity = dialogContext.Context.Activity; | |
switch (selectedCard) | |
{ | |
case "Update Profile": | |
//var updateProfileDialogContext = _updateProfileDialogs.CreateContext(_globalContext, GlobalState.UpdateProfile); | |
//await updateProfileDialogContext.Continue(); | |
await dialogContext.Replace(UpdateProfilePromptStep.GatherUserProfile); | |
break; | |
case "Help": | |
await dialogContext.Context.SendActivity(CreateResponse(activity, await HandleHelp())); | |
break; | |
default: | |
await dialogContext.Context.SendActivity(CreateResponse(activity, await HandleHelp())); | |
break; | |
} | |
await dialogContext.End(); | |
} | |
} | |
} |
This file contains hidden or 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
using System.Threading.Tasks; | |
using MyBot.Core.ViewModels; | |
using Microsoft.Bot; | |
using Microsoft.Bot.Builder; | |
using Microsoft.Bot.Builder.Core.Extensions; | |
using Microsoft.Bot.Builder.Dialogs; | |
using Microsoft.Bot.Builder.Prompts; | |
namespace MyBot.Api.Bot | |
{ | |
public static class UpdateProfilePromptStep | |
{ | |
public const string GatherUserProfile = "gatherUserProfile"; | |
public const string FirstNamePrompt = "firstName"; | |
public const string LastNamePrompt = "lastName"; | |
public const string PhoneNumberPrompt = "phoneNumber"; | |
} | |
public partial class MyBot : IBot | |
{ | |
private async Task FirstNameValidator(ITurnContext context, TextResult result) | |
{ | |
if (result.Value.Length <= 3) | |
{ | |
result.Status = PromptStatus.NotRecognized; | |
await context.SendActivity("Your name should be at least 3 characters long."); | |
} | |
} | |
private async Task LastNameValidator(ITurnContext context, TextResult result) | |
{ | |
if (result.Value.Length <= 3) | |
{ | |
result.Status = PromptStatus.NotRecognized; | |
await context.SendActivity("Your name should be at least 3 characters long."); | |
} | |
} | |
private async Task PhoneNumberValidator(ITurnContext context, TextResult result) | |
{ | |
if (result.Value.Length != 10) | |
{ | |
result.Status = PromptStatus.NotRecognized; | |
await context.SendActivity("Your phone number should be between 10 chars long."); | |
} | |
} | |
private async Task AskFirstNameStep(DialogContext dialogContext, object result, SkipStepFunction next) | |
{ | |
await dialogContext.Prompt(UpdateProfilePromptStep.FirstNamePrompt, "What is your first name?"); | |
} | |
private async Task AskLastNameStep(DialogContext dialogContext, object result, SkipStepFunction next) | |
{ | |
var state = dialogContext.Context.GetConversationState<GlobalState>(); | |
state.UpdateProfile.FirstName = (result as TextResult).Value; | |
await dialogContext.Prompt(UpdateProfilePromptStep.LastNamePrompt, "What is your last name?"); | |
} | |
private async Task AskPhoneNumberStep(DialogContext dialogContext, object result, SkipStepFunction next) | |
{ | |
var state = dialogContext.Context.GetConversationState<GlobalState>(); | |
state.UpdateProfile.LastName = (result as TextResult).Value; | |
await dialogContext.Prompt(UpdateProfilePromptStep.PhoneNumberPrompt, "What is your phone number?"); | |
} | |
private async Task GatherUserProfileStep(DialogContext dialogContext, object result, SkipStepFunction next) | |
{ | |
var state = dialogContext.Context.GetConversationState<GlobalState>(); | |
state.UpdateProfile.PhoneNumber = (result as TextResult).Value; | |
var from = dialogContext.Context.Activity.From; | |
var success = await _userProfileService.AddOrUpdate( | |
new UserInput | |
{ | |
Username = from.Id, | |
DisplayName = state.UpdateProfile.FullName ?? from.Name, | |
PhoneNumber = state.UpdateProfile.PhoneNumber | |
} | |
); | |
if (success) { | |
await dialogContext.Context.SendActivity($"Your full name is {state.UpdateProfile.FullName} and your phone number is {state.UpdateProfile.PhoneNumber}"); | |
} else { | |
await dialogContext.Context.SendActivity("An erorr has occured"); | |
} | |
await dialogContext.End(); | |
} | |
} | |
} |
This file contains hidden or 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
... | |
var credentialProvider = new SimpleCredentialProvider( | |
Configuration.GetSection(MicrosoftAppCredentials.MicrosoftAppIdKey)?.Value, | |
Configuration.GetSection(MicrosoftAppCredentials.MicrosoftAppPasswordKey)?.Value | |
); | |
services.AddBot<MyBot>(options => | |
{ | |
options.CredentialProvider = new ConfigurationCredentialProvider(Configuration); | |
// The CatchExceptionMiddleware provides a top-level exception handler for your bot. | |
// Any exceptions thrown by other Middleware, or by your OnTurn method, will be | |
// caught here. To facillitate debugging, the exception is sent out, via Trace, | |
// to the emulator. Trace activities are NOT displayed to users, so in addition | |
// an "Ooops" message is sent. | |
options.Middleware.Add(new CatchExceptionMiddleware<Exception>(async (context, exception) => | |
{ | |
await context.TraceActivity("EchoBot Exception", exception); | |
await context.SendActivity(exception.Message); // "Sorry, it looks like something went wrong!" | |
})); | |
// The Memory Storage used here is for local bot debugging only. When the bot | |
// is restarted, anything stored in memory will be gone. | |
IStorage dataStore = new MemoryStorage(); | |
// The File data store, shown here, is suitable for bots that run on | |
// a single machine and need durable state across application restarts. | |
// IStorage dataStore = new FileStorage(System.IO.Path.GetTempPath()); | |
// For production bots use the Azure Table Store, Azure Blob, or | |
// Azure CosmosDB storage provides, as seen below. To include any of | |
// the Azure based storage providers, add the Microsoft.Bot.Builder.Azure | |
// Nuget package to your solution. That package is found at: | |
// https://www.nuget.org/packages/Microsoft.Bot.Builder.Azure/ | |
// IStorage dataStore = new Microsoft.Bot.Builder.Azure.AzureTableStorage("AzureTablesConnectionString", "TableName"); | |
// IStorage dataStore = new Microsoft.Bot.Builder.Azure.AzureBlobStorage("AzureBlobConnectionString", "containerName"); | |
options.Middleware.Add(new ConversationState<GlobalState>(dataStore)); | |
}); | |
... |
This file contains hidden or 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
using System.Collections.Generic; | |
namespace MyBot.Api.Bot | |
{ | |
public class UpdateProfileState : Dictionary<string, object> | |
{ | |
private const string FirstNameKey = "firstName"; | |
private const string LastNameKey = "lastName"; | |
private const string PhoneNumberKey = "phoneNumber"; | |
public UpdateProfileState() | |
{ | |
this[FirstNameKey] = null; | |
this[LastNameKey] = null; | |
this[PhoneNumberKey] = null; | |
} | |
public string FirstName | |
{ | |
get { return (string)this[FirstNameKey]; } | |
set { this[FirstNameKey] = value; } | |
} | |
public string LastName | |
{ | |
get { return (string)this[LastNameKey]; } | |
set { this[LastNameKey] = value; } | |
} | |
public string PhoneNumber | |
{ | |
get { return (string)this[PhoneNumberKey]; } | |
set { this[PhoneNumberKey] = value; } | |
} | |
public string FullName | |
{ | |
get { return string.Format("{0} {1}", (string)this[FirstNameKey], (string)this[LastNameKey]); } | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment