SlideShare a Scribd company logo
Bot Builder v4 HOL
Chapter 1: Preparation
• Visual Studio 2017
• .Net Core 2.x ( https://www.microsoft.com/net/download )
• Bot Builder V4 SDK Template for Visual Studio
( https://marketplace.visualstudio.com/items?itemName=BotBuilder.botb
uilderv4 )
• Bot Emulator ( https://github.com/Microsoft/BotFramework-
Emulator/releases )
• Ngrok (https://ngrok.com/ )
Or
You can use Azure Bot Services .
After Download the Ngrok, please launch your Bot Emulator and go to
Emulator Settings and Setup as below.
Chapter 2: Create Project
1. Launch your Visual Studio.
2. Create a New Project, File > New > Project
3. Select Multi-Project and Select Bot Builder Echo Bot v4 and Name
your project, HandsonBot . Lastly , click OK button.
4. Make sure your bot template update to the v4.0.7
5. Test Run your project. Go to dropdownlist of IIS Express and Select
HandsonBot , which is your project name and Press F5 for Debug your
project.
6. After Debug, you will see the localhost as below.
7. Launch your Bot Framework Emulator and Click Open Bot and Select
BotConfiguration.Bot that in your project.
Chapter 3: Implementation of welcome message
1. Go to your project, right click > add new folder “SampleBot”.
2. After create the folder , right click > add class > name the class as
“SampleBot”.
3. Implement the code as below.
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Schema;
using Microsoft.Extensions.Logging;
using System;
using System.Threading;
using System.Threading.Tasks;
namespace HandsonBot.SampleBot
{
public class SampleBot : IBot
{
private const string WelcomeText = "Welcome to Sample Bot";
private readonly ILogger _logger;
private readonly SampleBotAccessors _accessors; // Added
private readonly DialogSet _dialogs; // Added
public SampleBot(SampleBotAccessors accessors, ILoggerFactory
loggerFactory) // Updated
{
_accessors = accessors ?? throw new
ArgumentException(nameof(accessors)); // Added
_dialogs = new DialogSet(accessors.ConversationDialogState); // Added
_dialogs.Add(new TextPrompt("name", ValidateHandleNameAsync));
_logger = loggerFactory.CreateLogger<SampleBot>();
_logger.LogInformation("Start SampleBot");
}
private Task<bool> ValidateHandleNameAsync(PromptValidatorContext<string>
promptContext, CancellationToken cancellationToken)
{
var result = promptContext.Recognized.Value;
if (result != null && result.Length >= 3)
{
var upperValue = result.ToUpperInvariant();
promptContext.Recognized.Value = upperValue;
return Task.FromResult(true);
}
return Task.FromResult(false);
}
public async Task OnTurnAsync(ITurnContext turnContext, CancellationToken
cancellationToken = default(CancellationToken))
{
if (turnContext.Activity.Type == ActivityTypes.Message)
{
// We will exchange normal messages here.
await SendMessageActivityAsync(turnContext, cancellationToken); //
updated
}
else if (turnContext.Activity.Type ==
ActivityTypes.ConversationUpdate)
{
await SendWelcomeMessageAsync(turnContext, cancellationToken);
}
else
{
_logger.LogInformation($"passed:{turnContext.Activity.Type}");
}
await _accessors.ConversationState.SaveChangesAsync(turnContext,
false, cancellationToken);
await _accessors.UserState.SaveChangesAsync(turnContext, false,
cancellationToken);
}
private static async Task SendWelcomeMessageAsync(ITurnContext
turnContext, CancellationToken cancellationToken)
{
foreach (var member in turnContext.Activity.MembersAdded)
{
if (member.Id != turnContext.Activity.Recipient.Id)
{
await turnContext.SendActivityAsync(WelcomeText,
cancellationToken: cancellationToken);
}
}
}
private async Task SendMessageActivityAsync(ITurnContext turnContext,
CancellationToken cancellationToken)
{
var dialogContext = await _dialogs.CreateContextAsync(turnContext,
cancellationToken);
var dialogTurnResult = await
dialogContext.ContinueDialogAsync(cancellationToken);
var userProfile = await _accessors.UserProfile.GetAsync(turnContext,
() => new UserProfile(), cancellationToken);
// If the handle name is not registered in UserState
if (userProfile.HandleName == null)
{
await GetHandleNameAsync(dialogContext, dialogTurnResult,
userProfile, cancellationToken);
}
else
{
await turnContext.SendActivityAsync($"Hello
{userProfile.HandleName}", cancellationToken: cancellationToken);
}
}
private async Task GetHandleNameAsync(DialogContext dialogContext,
DialogTurnResult dialogTurnResult, UserProfile userProfile, CancellationToken
cancellationToken)
{
if (dialogTurnResult.Status is DialogTurnStatus.Empty)
{
await dialogContext.PromptAsync(
"name",
new PromptOptions
{
Prompt = MessageFactory.Text("Please tell me your handle
name first."),
RetryPrompt = MessageFactory.Text("The handle name must be
at least 3 words long."),
},
cancellationToken);
}
else if (dialogTurnResult.Status is DialogTurnStatus.Complete)
{
// Register your handle name with UserState
userProfile.HandleName = (string)dialogTurnResult.Result;
_logger.LogInformation($"Handle Name registration:
{userProfile.HandleName}");
}
}
}
}
4. Go to Startup.cs
Change near line 57
From
services.AddBot<EchoWithCounterBot>(options =>
to
services.AddBot<SampleBot.SampleBot>(options =>
&
Change near line 78
From
ILogger logger = _loggerFactory.CreateLogger<EchoWithCounterBot>();
to
ILogger logger = _loggerFactory.CreateLogger<SampleBot.SampleBot>();
Chapter 4: State Management
1. Right Click “SampleBot” Folder > right click > Add > Create
UserProfile.class and Implement the code as below.
namespace HandsonBot.SampleBot
{
public class UserProfile
{
public string HandleName { get; set; }
}
}
2. Install the Microsoft.Bot.Builder.Dialogs Nuget Package.
Go to Tools> Nuget Package Manager> Package Manager Console and
type the command as below.
Install-Package Microsoft.Bot.Builder.Dialogs
3. Implement of State management Accessor
Right Click “SampleBot” Folder > right click > Add > Create
SampleBotAccessors.class and Implement the code as below.
using System;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Dialogs;
namespace HandsonBot.SampleBot
{
public class SampleBotAccessors
{
public IStatePropertyAccessor<DialogState> ConversationDialogState { get;
set; }
public IStatePropertyAccessor<UserProfile> UserProfile { get; set; }
public ConversationState ConversationState { get; }
public UserState UserState { get; }
public SampleBotAccessors(ConversationState conversationState, UserState
userState)
{
ConversationState = conversationState ?? throw new
ArgumentNullException(nameof(conversationState));
UserState = userState ?? throw new ArgumentException(nameof(userState));
}
}
}
4. Change Startup.cs near line 111, to implement Application of UserState
Class
From
var conversationState = new ConversationState(dataStore);
options.State.Add(conversationState);
To
var conversationState = new ConversationState(dataStore);
options.State.Add(conversationState);
var userState = new UserState(dataStore);
options.State.Add(userState);
5. Implement of SampleBotAccessors class in Startup.cs , add the following
code as below.
using System;
using System.Linq;
using HandsonBot.SampleBot;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Builder.Integration;
using Microsoft.Bot.Builder.Integration.AspNet.Core;
using Microsoft.Bot.Configuration;
using Microsoft.Bot.Connector.Authentication;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace HandsonBot
{
/// <summary>
/// The Startup class configures services and the request
pipeline.
/// </summary>
public class Startup
{
private ILoggerFactory _loggerFactory;
private bool _isProduction = false;
public Startup(IHostingEnvironment env)
{
_isProduction = env.IsProduction();
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true,
reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json"
, optional: true)
.AddEnvironmentVariables();
Configuration = builder.Build();
}
/// <summary>
/// Gets the configuration that represents a set of key/value
application configuration properties.
/// </summary>
/// <value>
/// The <see cref="IConfiguration"/> that represents a set of
key/value application configuration properties.
/// </value>
public IConfiguration Configuration { get; }
/// <summary>
/// This method gets called by the runtime. Use this method to
add services to the container.
/// </summary>
/// <param name="services">The <see
cref="IServiceCollection"/> specifies the contract for a collection of
service descriptors.</param>
/// <seealso cref="IStatePropertyAccessor{T}"/>
/// <seealso cref="https://docs.microsoft.com/en-
us/aspnet/web-api/overview/advanced/dependency-injection"/>
/// <seealso cref="https://docs.microsoft.com/en-us/azure/bot-
service/bot-service-manage-channels?view=azure-bot-service-4.0"/>
public void ConfigureServices(IServiceCollection services)
{
services.AddBot<SampleBot.SampleBot>(options =>
{
var secretKey =
Configuration.GetSection("botFileSecret")?.Value;
var botFilePath =
Configuration.GetSection("botFilePath")?.Value;
// Loads .bot configuration file and adds a singleton
that your Bot can access through dependency injection.
var botConfig = BotConfiguration.Load(botFilePath ??
@".BotConfiguration.bot", secretKey);
services.AddSingleton(sp => botConfig ?? throw new
InvalidOperationException($"The .bot config file could not be loaded.
({botConfig})"));
// Retrieve current endpoint.
var environment = _isProduction ? "production" :
"development";
var service = botConfig.Services.Where(s => s.Type ==
"endpoint" && s.Name == environment).FirstOrDefault();
if (!(service is EndpointService endpointService))
{
throw new InvalidOperationException($"The .bot
file does not contain an endpoint with name '{environment}'.");
}
options.CredentialProvider = new
SimpleCredentialProvider(endpointService.AppId,
endpointService.AppPassword);
// Creates a logger for the application to use.
ILogger logger =
_loggerFactory.CreateLogger<SampleBot.SampleBot>();
// Catches any errors that occur during a conversation
turn and logs them.
options.OnTurnError = async (context, exception) =>
{
logger.LogError($"Exception caught :
{exception}");
await context.SendActivityAsync("Sorry, it looks
like something went wrong.");
};
// The Memory Storage used here is for local bot
debugging only. When the bot
// is restarted, everything stored in memory will be
gone.
IStorage dataStore = new MemoryStorage();
// For production bots use the Azure Blob or
// Azure CosmosDB storage providers. For 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/
// Uncomment the following lines to use Azure Blob
Storage
// //Storage configuration name or ID from the .bot
file.
// const string StorageConfigurationId = "<STORAGE-
NAME-OR-ID-FROM-BOT-FILE>";
// var blobConfig =
botConfig.FindServiceByNameOrId(StorageConfigurationId);
// if (!(blobConfig is BlobStorageService
blobStorageConfig))
// {
// throw new InvalidOperationException($"The .bot
file does not contain an blob storage with name
'{StorageConfigurationId}'.");
// }
// // Default container name.
// const string DefaultBotContainer = "<DEFAULT-
CONTAINER>";
// var storageContainer =
string.IsNullOrWhiteSpace(blobStorageConfig.Container) ?
DefaultBotContainer : blobStorageConfig.Container;
// IStorage dataStore = new
Microsoft.Bot.Builder.Azure.AzureBlobStorage(blobStorageConfig.Connect
ionString, storageContainer);
// Create Conversation State object.
// The Conversation State object is where we persist
anything at the conversation-scope.
var conversationState = new
ConversationState(dataStore);
options.State.Add(conversationState);
var userState = new UserState(dataStore);
options.State.Add(userState);
});
// Create and register state accesssors.
// Acessors created here are passed into the IBot-derived
class on every turn.
services.AddSingleton<EchoBotAccessors>(sp =>
{
var options =
sp.GetRequiredService<IOptions<BotFrameworkOptions>>().Value;
if (options == null)
{
throw new
InvalidOperationException("BotFrameworkOptions must be configured
prior to setting up the state accessors");
}
var conversationState =
options.State.OfType<ConversationState>().FirstOrDefault();
if (conversationState == null)
{
throw new
InvalidOperationException("ConversationState must be defined and added
before adding conversation-scoped state accessors.");
}
// Create the custom state accessor.
// State accessors enable other components to read and
write individual properties of state.
var accessors = new
EchoBotAccessors(conversationState)
{
CounterState =
conversationState.CreateProperty<CounterState>(EchoBotAccessors.Counte
rStateName),
};
return accessors;
});
// Create and register state accesssors.
// Acessors created here are passed into the IBot-derived
class on every turn.
services.AddSingleton<SampleBotAccessors>(sp =>
{
var options =
sp.GetRequiredService<IOptions<BotFrameworkOptions>>().Value
?? throw new
InvalidOperationException("BotFrameworkOptions must be configured
prior to setting up the state accessors");
var conversationState =
options.State.OfType<ConversationState>().FirstOrDefault()
?? throw new
InvalidOperationException("ConversationState が ConfigureServices で設
定されていません。");
var userState =
options.State.OfType<UserState>().FirstOrDefault()
?? throw new
InvalidOperationException("UserState が ConfigureServices で設定されてい
ません。");
var accessors = new
SampleBotAccessors(conversationState, userState)
{
ConversationDialogState =
conversationState.CreateProperty<DialogState>(nameof(DialogState)),
UserProfile =
userState.CreateProperty<UserProfile>(nameof(UserProfile)),
};
return accessors;
});
}
public void Configure(IApplicationBuilder app,
IHostingEnvironment env, ILoggerFactory loggerFactory)
{
_loggerFactory = loggerFactory;
app.UseDefaultFiles()
.UseStaticFiles()
.UseBotFramework();
}
}
}
6. Go back to SampleBot.cs added the following code with comment and
Waterfallsteps.
//Added: Part where code is added
//Updated: Changing parts
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Schema;
using Microsoft.Extensions.Logging;
using System;
using System.Threading;
using System.Threading.Tasks;
namespace HandsonBot.SampleBot
{
public class SampleBot : IBot
{
private const string WelcomeText = "Welcome to Sample Bot";
private readonly ILogger _logger;
private readonly SampleBotAccessors _accessors; // Added
private readonly DialogSet _dialogs; // Added
public SampleBot(SampleBotAccessors accessors, ILoggerFactory
loggerFactory)
{
_accessors = accessors ?? throw new
ArgumentException(nameof(accessors));
_dialogs = new DialogSet(accessors.ConversationDialogState);
var waterfallSteps = new WaterfallStep[]
{
ConfirmAgeStepAsync,
ExecuteAgeStepAsync,
ExecuteFinalConfirmStepAsync,
ExecuteSummaryStepAsync,
};
_dialogs.Add(new TextPrompt("name", ValidateHandleNameAsync));
_dialogs.Add(new ConfirmPrompt("confirm"));
_dialogs.Add(new NumberPrompt<int>("age"));
_dialogs.Add(new WaterfallDialog("details", waterfallSteps));
_logger = loggerFactory.CreateLogger<SampleBot>();
_logger.LogInformation("Start SampleBot");
}
private async Task GetHandleNameAsync(DialogContext dialogContext,
DialogTurnResult dialogTurnResult, UserProfile userProfile,
CancellationToken cancellationToken)
{
if (dialogTurnResult.Status is DialogTurnStatus.Empty)
{
await dialogContext.PromptAsync(
"name",
new PromptOptions
{
Prompt = MessageFactory.Text("Please tell me your
handle name first."),
RetryPrompt = MessageFactory.Text("The handle name
must be at least 3 words long."),
},
cancellationToken);
}
// If you enter a handle name
else if (dialogTurnResult.Status is DialogTurnStatus.Complete)
{
if (dialogTurnResult.Result != null)
{
// Register your handle name with UserState
userProfile.HandleName =
(string)dialogTurnResult.Result;
await dialogContext.BeginDialogAsync("details", null,
cancellationToken); // added
}
}
}
private Task<bool>
ValidateHandleNameAsync(PromptValidatorContext<string> promptContext,
CancellationToken cancellationToken)
{
var result = promptContext.Recognized.Value;
if (result != null && result.Length >= 3)
{
var upperValue = result.ToUpperInvariant();
promptContext.Recognized.Value = upperValue;
return Task.FromResult(true);
}
return Task.FromResult(false);
}
public async Task OnTurnAsync(ITurnContext turnContext,
CancellationToken cancellationToken = default(CancellationToken))
{
if (turnContext.Activity.Type == ActivityTypes.Message)
{
// We will exchange normal messages here.
await SendMessageActivityAsync(turnContext,
cancellationToken); // updated
}
else if (turnContext.Activity.Type ==
ActivityTypes.ConversationUpdate)
{
await SendWelcomeMessageAsync(turnContext,
cancellationToken);
}
else
{
_logger.LogInformation($"passed:{turnContext.Activity.Type}");
}
await
_accessors.ConversationState.SaveChangesAsync(turnContext, false,
cancellationToken);
await _accessors.UserState.SaveChangesAsync(turnContext, false,
cancellationToken);
}
private static async Task SendWelcomeMessageAsync(ITurnContext
turnContext, CancellationToken cancellationToken)
{
foreach (var member in turnContext.Activity.MembersAdded)
{
if (member.Id != turnContext.Activity.Recipient.Id)
{
await turnContext.SendActivityAsync(WelcomeText,
cancellationToken: cancellationToken);
}
}
}
public async Task SendMessageActivityAsync(ITurnContext
turnContext, CancellationToken cancellationToken)
{
var dialogContext = await
_dialogs.CreateContextAsync(turnContext, cancellationToken);
var dialogTurnResult = await
dialogContext.ContinueDialogAsync(cancellationToken);
var userProfile = await
_accessors.UserProfile.GetAsync(turnContext, () => new UserProfile(),
cancellationToken);
// If the handle name is not registered in UserState
if (userProfile.HandleName == null)
{
await GetHandleNameAsync(dialogContext, dialogTurnResult,
userProfile, cancellationToken);
}
// If you have a handle name registered with UserState
else
{
// added
if (dialogTurnResult.Status == DialogTurnStatus.Empty)
{
await dialogContext.BeginDialogAsync("details", null,
cancellationToken);
}
}
}
//----------------------------------------------
//Lab
private async Task<DialogTurnResult>
ConfirmAgeStepAsync(WaterfallStepContext stepContext, CancellationToken
cancellationToken)
{
var userProfile = await
_accessors.UserProfile.GetAsync(stepContext.Context, () => new
UserProfile(), cancellationToken);
return await stepContext.PromptAsync(
"confirm",
new PromptOptions
{
Prompt = MessageFactory.Text($"{userProfile.HandleName}
May I ask your age?"),
RetryPrompt = MessageFactory.Text("Answer yes or No."),
},
cancellationToken);
}
private async Task<DialogTurnResult>
ExecuteAgeStepAsync(WaterfallStepContext stepContext, CancellationToken
cancellationToken)
{
if ((bool)stepContext.Result)
{
return await stepContext.PromptAsync(
"age",
new PromptOptions
{
Prompt = MessageFactory.Text("What is your age?"),
RetryPrompt = MessageFactory.Text("Enter the age in
numbers."),
},
cancellationToken);
}
else
{
return await stepContext.NextAsync(-1, cancellationToken);
}
}
private async Task<DialogTurnResult>
ExecuteFinalConfirmStepAsync(WaterfallStepContext stepContext,
CancellationToken cancellationToken)
{
var userProfile = await
_accessors.UserProfile.GetAsync(stepContext.Context, () => new
UserProfile(), cancellationToken);
userProfile.Age = (int)stepContext.Result;
var message = GetAgeAcceptedMessage(userProfile);
await stepContext.Context.SendActivityAsync(message,
cancellationToken);
return await stepContext.PromptAsync(
"confirm",
new PromptOptions { Prompt = MessageFactory.Text("Is this
the registration information you want?") },
cancellationToken);
}
private static IActivity GetAgeAcceptedMessage(UserProfile
userProfile)
{
return MessageFactory.Text(userProfile.Age == -1 ? "Age is
private, isn't it?" : $"I'm {userProfile.Age} year old.");
}
private async Task<DialogTurnResult>
ExecuteSummaryStepAsync(WaterfallStepContext stepContext, CancellationToken
cancellationToken)
{
if ((bool)stepContext.Result)
{
var userProfile = await
_accessors.UserProfile.GetAsync(stepContext.Context, () => new
UserProfile(), cancellationToken);
var summaryMessages = GetSummaryMessages(userProfile);
await
stepContext.Context.SendActivitiesAsync(summaryMessages,
cancellationToken);
// End of Detail dialog
return await stepContext.EndDialogAsync(cancellationToken:
cancellationToken);
}
else
{
// Redo the Details dialog.
await
stepContext.Context.SendActivityAsync(MessageFactory.Text("I will visit you
again."), cancellationToken);
return await stepContext.ReplaceDialogAsync("details",
cancellationToken: cancellationToken);
}
}
private static IActivity[] GetSummaryMessages(UserProfile
userProfile)
{
IActivity summaryMessage = MessageFactory.Text(userProfile.Age
== -1
? $"{userProfile.HandleName} Your age is private."
: $"{userProfile.HandleName} , {userProfile.Age} year
old.");
IActivity thanksMessage = MessageFactory.Text("Thank you for
your input.");
return new[] { summaryMessage, thanksMessage };
}
//---------------------------
}
}
Chapter 5: Implementation Age Property
1. Go to UserProfile.cs , implement Age Property as shown below.
namespace HandsonBot.SampleBot
{
public class UserProfile
{
public string HandleName { get; set; }
public int Age { get; set; }
}
}
2. Debug your application again.
Happy Coding !!!

More Related Content

What's hot

Legacy Code Kata v3.0
Legacy Code Kata v3.0Legacy Code Kata v3.0
Legacy Code Kata v3.0
William Munn
 
Legacy Dependency Kata v2.0
Legacy Dependency Kata v2.0Legacy Dependency Kata v2.0
Legacy Dependency Kata v2.0
William Munn
 
JavaScript Refactoring
JavaScript RefactoringJavaScript Refactoring
JavaScript Refactoring
Krzysztof Szafranek
 
The Ring programming language version 1.5.4 book - Part 14 of 185
The Ring programming language version 1.5.4 book - Part 14 of 185The Ring programming language version 1.5.4 book - Part 14 of 185
The Ring programming language version 1.5.4 book - Part 14 of 185
Mahmoud Samir Fayed
 
Qunit Java script Un
Qunit Java script UnQunit Java script Un
Qunit Java script Un
akanksha arora
 
Stop Making Excuses and Start Testing Your JavaScript
Stop Making Excuses and Start Testing Your JavaScriptStop Making Excuses and Start Testing Your JavaScript
Stop Making Excuses and Start Testing Your JavaScript
Ryan Anklam
 
Auto-GWT : Better GWT Programming with Xtend
Auto-GWT : Better GWT Programming with XtendAuto-GWT : Better GWT Programming with Xtend
Auto-GWT : Better GWT Programming with Xtend
Sven Efftinge
 
Workflow Foundation 4
Workflow Foundation 4Workflow Foundation 4
Workflow Foundation 4
Robert MacLean
 
Gwt and Xtend
Gwt and XtendGwt and Xtend
Gwt and Xtend
Sven Efftinge
 
Test Driven Development
Test Driven DevelopmentTest Driven Development
Test Driven Development
Anand Kumar Rajana
 
Call Back
Call BackCall Back
Call Back
leminhvuong
 
2. Design patterns. part #2
2. Design patterns. part #22. Design patterns. part #2
2. Design patterns. part #2
Leonid Maslov
 
From object oriented to functional domain modeling
From object oriented to functional domain modelingFrom object oriented to functional domain modeling
From object oriented to functional domain modeling
Codemotion
 
DRYing to Monad in Java8
DRYing to Monad in Java8DRYing to Monad in Java8
DRYing to Monad in Java8
Dhaval Dalal
 
J query
J queryJ query
Tomasz Polanski - Automated mobile testing 2016 - Testing: why, when, how
Tomasz Polanski - Automated mobile testing 2016 - Testing: why, when, howTomasz Polanski - Automated mobile testing 2016 - Testing: why, when, how
Tomasz Polanski - Automated mobile testing 2016 - Testing: why, when, how
Tomasz Polanski
 
Soaoui
SoaouiSoaoui
Soaoui
Vikas Singh
 
The Ring programming language version 1.2 book - Part 5 of 84
The Ring programming language version 1.2 book - Part 5 of 84The Ring programming language version 1.2 book - Part 5 of 84
The Ring programming language version 1.2 book - Part 5 of 84
Mahmoud Samir Fayed
 
Dive into SObjectizer 5.5. Introductory part
Dive into SObjectizer 5.5. Introductory partDive into SObjectizer 5.5. Introductory part
Dive into SObjectizer 5.5. Introductory part
Yauheni Akhotnikau
 
Mockito intro
Mockito introMockito intro
Mockito intro
Jonathan Holloway
 

What's hot (20)

Legacy Code Kata v3.0
Legacy Code Kata v3.0Legacy Code Kata v3.0
Legacy Code Kata v3.0
 
Legacy Dependency Kata v2.0
Legacy Dependency Kata v2.0Legacy Dependency Kata v2.0
Legacy Dependency Kata v2.0
 
JavaScript Refactoring
JavaScript RefactoringJavaScript Refactoring
JavaScript Refactoring
 
The Ring programming language version 1.5.4 book - Part 14 of 185
The Ring programming language version 1.5.4 book - Part 14 of 185The Ring programming language version 1.5.4 book - Part 14 of 185
The Ring programming language version 1.5.4 book - Part 14 of 185
 
Qunit Java script Un
Qunit Java script UnQunit Java script Un
Qunit Java script Un
 
Stop Making Excuses and Start Testing Your JavaScript
Stop Making Excuses and Start Testing Your JavaScriptStop Making Excuses and Start Testing Your JavaScript
Stop Making Excuses and Start Testing Your JavaScript
 
Auto-GWT : Better GWT Programming with Xtend
Auto-GWT : Better GWT Programming with XtendAuto-GWT : Better GWT Programming with Xtend
Auto-GWT : Better GWT Programming with Xtend
 
Workflow Foundation 4
Workflow Foundation 4Workflow Foundation 4
Workflow Foundation 4
 
Gwt and Xtend
Gwt and XtendGwt and Xtend
Gwt and Xtend
 
Test Driven Development
Test Driven DevelopmentTest Driven Development
Test Driven Development
 
Call Back
Call BackCall Back
Call Back
 
2. Design patterns. part #2
2. Design patterns. part #22. Design patterns. part #2
2. Design patterns. part #2
 
From object oriented to functional domain modeling
From object oriented to functional domain modelingFrom object oriented to functional domain modeling
From object oriented to functional domain modeling
 
DRYing to Monad in Java8
DRYing to Monad in Java8DRYing to Monad in Java8
DRYing to Monad in Java8
 
J query
J queryJ query
J query
 
Tomasz Polanski - Automated mobile testing 2016 - Testing: why, when, how
Tomasz Polanski - Automated mobile testing 2016 - Testing: why, when, howTomasz Polanski - Automated mobile testing 2016 - Testing: why, when, how
Tomasz Polanski - Automated mobile testing 2016 - Testing: why, when, how
 
Soaoui
SoaouiSoaoui
Soaoui
 
The Ring programming language version 1.2 book - Part 5 of 84
The Ring programming language version 1.2 book - Part 5 of 84The Ring programming language version 1.2 book - Part 5 of 84
The Ring programming language version 1.2 book - Part 5 of 84
 
Dive into SObjectizer 5.5. Introductory part
Dive into SObjectizer 5.5. Introductory partDive into SObjectizer 5.5. Introductory part
Dive into SObjectizer 5.5. Introductory part
 
Mockito intro
Mockito introMockito intro
Mockito intro
 

Similar to Bot builder v4 HOL

Pushing the Web: Interesting things to Know
Pushing the Web: Interesting things to KnowPushing the Web: Interesting things to Know
Pushing the Web: Interesting things to Know
n|u - The Open Security Community
 
比XML更好用的Java Annotation
比XML更好用的Java Annotation比XML更好用的Java Annotation
比XML更好用的Java Annotation
javatwo2011
 
Speed up your Web applications with HTML5 WebSockets
Speed up your Web applications with HTML5 WebSocketsSpeed up your Web applications with HTML5 WebSockets
Speed up your Web applications with HTML5 WebSockets
Yakov Fain
 
Google Web Toolkits
Google Web ToolkitsGoogle Web Toolkits
Google Web Toolkits
Yiguang Hu
 
A GWT Application with MVP Pattern Deploying to CloudFoundry using Spring Roo
A GWT Application with MVP Pattern Deploying to CloudFoundry using  Spring Roo A GWT Application with MVP Pattern Deploying to CloudFoundry using  Spring Roo
A GWT Application with MVP Pattern Deploying to CloudFoundry using Spring Roo
Ali Parmaksiz
 
softshake 2014 - Java EE
softshake 2014 - Java EEsoftshake 2014 - Java EE
softshake 2014 - Java EE
Alexis Hassler
 
How to build twitter bot using golang from scratch
How to build twitter bot using golang from scratchHow to build twitter bot using golang from scratch
How to build twitter bot using golang from scratch
Katy Slemon
 
Murach : How to work with session state and cookies
Murach : How to work with session state and cookiesMurach : How to work with session state and cookies
Murach : How to work with session state and cookies
MahmoudOHassouna
 
Clean coding-practices
Clean coding-practicesClean coding-practices
Clean coding-practices
John Ferguson Smart Limited
 
How to perform debounce in react
How to perform debounce in reactHow to perform debounce in react
How to perform debounce in react
BOSC Tech Labs
 
Google Web Toolkits
Google Web ToolkitsGoogle Web Toolkits
Google Web Toolkits
Yiguang Hu
 
VPN Access Runbook
VPN Access RunbookVPN Access Runbook
VPN Access Runbook
Taha Shakeel
 
Tech Talk: App Functionality (Android)
Tech Talk: App Functionality (Android)Tech Talk: App Functionality (Android)
Tech Talk: App Functionality (Android)
Lifeparticle
 
09 Application Design
09 Application Design09 Application Design
09 Application Design
Ranjan Kumar
 
2008 - TechDays PT: WCF, JSON and AJAX for performance and manageability
2008 - TechDays PT: WCF, JSON and AJAX for performance and manageability2008 - TechDays PT: WCF, JSON and AJAX for performance and manageability
2008 - TechDays PT: WCF, JSON and AJAX for performance and manageability
Daniel Fisher
 
JavaScript Lessons 2023
JavaScript Lessons 2023JavaScript Lessons 2023
JavaScript Lessons 2023
Laurence Svekis ✔
 
Tdd iPhone For Dummies
Tdd iPhone For DummiesTdd iPhone For Dummies
Tdd iPhone For Dummies
Giordano Scalzo
 
Workshop 23: ReactJS, React & Redux testing
Workshop 23: ReactJS, React & Redux testingWorkshop 23: ReactJS, React & Redux testing
Workshop 23: ReactJS, React & Redux testing
Visual Engineering
 
Annotation processing
Annotation processingAnnotation processing
Annotation processing
Florent Champigny
 
Functional Vaadin talk at OSCON 2014
Functional Vaadin talk at OSCON 2014Functional Vaadin talk at OSCON 2014
Functional Vaadin talk at OSCON 2014
hezamu
 

Similar to Bot builder v4 HOL (20)

Pushing the Web: Interesting things to Know
Pushing the Web: Interesting things to KnowPushing the Web: Interesting things to Know
Pushing the Web: Interesting things to Know
 
比XML更好用的Java Annotation
比XML更好用的Java Annotation比XML更好用的Java Annotation
比XML更好用的Java Annotation
 
Speed up your Web applications with HTML5 WebSockets
Speed up your Web applications with HTML5 WebSocketsSpeed up your Web applications with HTML5 WebSockets
Speed up your Web applications with HTML5 WebSockets
 
Google Web Toolkits
Google Web ToolkitsGoogle Web Toolkits
Google Web Toolkits
 
A GWT Application with MVP Pattern Deploying to CloudFoundry using Spring Roo
A GWT Application with MVP Pattern Deploying to CloudFoundry using  Spring Roo A GWT Application with MVP Pattern Deploying to CloudFoundry using  Spring Roo
A GWT Application with MVP Pattern Deploying to CloudFoundry using Spring Roo
 
softshake 2014 - Java EE
softshake 2014 - Java EEsoftshake 2014 - Java EE
softshake 2014 - Java EE
 
How to build twitter bot using golang from scratch
How to build twitter bot using golang from scratchHow to build twitter bot using golang from scratch
How to build twitter bot using golang from scratch
 
Murach : How to work with session state and cookies
Murach : How to work with session state and cookiesMurach : How to work with session state and cookies
Murach : How to work with session state and cookies
 
Clean coding-practices
Clean coding-practicesClean coding-practices
Clean coding-practices
 
How to perform debounce in react
How to perform debounce in reactHow to perform debounce in react
How to perform debounce in react
 
Google Web Toolkits
Google Web ToolkitsGoogle Web Toolkits
Google Web Toolkits
 
VPN Access Runbook
VPN Access RunbookVPN Access Runbook
VPN Access Runbook
 
Tech Talk: App Functionality (Android)
Tech Talk: App Functionality (Android)Tech Talk: App Functionality (Android)
Tech Talk: App Functionality (Android)
 
09 Application Design
09 Application Design09 Application Design
09 Application Design
 
2008 - TechDays PT: WCF, JSON and AJAX for performance and manageability
2008 - TechDays PT: WCF, JSON and AJAX for performance and manageability2008 - TechDays PT: WCF, JSON and AJAX for performance and manageability
2008 - TechDays PT: WCF, JSON and AJAX for performance and manageability
 
JavaScript Lessons 2023
JavaScript Lessons 2023JavaScript Lessons 2023
JavaScript Lessons 2023
 
Tdd iPhone For Dummies
Tdd iPhone For DummiesTdd iPhone For Dummies
Tdd iPhone For Dummies
 
Workshop 23: ReactJS, React & Redux testing
Workshop 23: ReactJS, React & Redux testingWorkshop 23: ReactJS, React & Redux testing
Workshop 23: ReactJS, React & Redux testing
 
Annotation processing
Annotation processingAnnotation processing
Annotation processing
 
Functional Vaadin talk at OSCON 2014
Functional Vaadin talk at OSCON 2014Functional Vaadin talk at OSCON 2014
Functional Vaadin talk at OSCON 2014
 

More from Cheah Eng Soon

Microsoft Defender for Endpoint
Microsoft Defender for EndpointMicrosoft Defender for Endpoint
Microsoft Defender for Endpoint
Cheah Eng Soon
 
Azure Active Directory - Secure and Govern
Azure Active Directory - Secure and GovernAzure Active Directory - Secure and Govern
Azure Active Directory - Secure and Govern
Cheah Eng Soon
 
Microsoft Zero Trust
Microsoft Zero TrustMicrosoft Zero Trust
Microsoft Zero Trust
Cheah Eng Soon
 
MEM for OnPrem Environments
MEM for OnPrem EnvironmentsMEM for OnPrem Environments
MEM for OnPrem Environments
Cheah Eng Soon
 
Microsoft Threat Protection Automated Incident Response
Microsoft Threat Protection Automated Incident Response Microsoft Threat Protection Automated Incident Response
Microsoft Threat Protection Automated Incident Response
Cheah Eng Soon
 
Azure Penetration Testing
Azure Penetration TestingAzure Penetration Testing
Azure Penetration Testing
Cheah Eng Soon
 
Azure Penetration Testing
Azure Penetration TestingAzure Penetration Testing
Azure Penetration Testing
Cheah Eng Soon
 
Penetration Testing Azure for Ethical Hackers
Penetration Testing Azure for Ethical HackersPenetration Testing Azure for Ethical Hackers
Penetration Testing Azure for Ethical Hackers
Cheah Eng Soon
 
Microsoft Threat Protection Automated Incident Response Demo
Microsoft Threat Protection Automated Incident Response DemoMicrosoft Threat Protection Automated Incident Response Demo
Microsoft Threat Protection Automated Incident Response Demo
Cheah Eng Soon
 
Microsoft Secure Score Demo
Microsoft Secure Score DemoMicrosoft Secure Score Demo
Microsoft Secure Score Demo
Cheah Eng Soon
 
Microsoft Cloud App Security Demo
Microsoft Cloud App Security DemoMicrosoft Cloud App Security Demo
Microsoft Cloud App Security Demo
Cheah Eng Soon
 
M365 Attack Simulation Demo
M365 Attack Simulation DemoM365 Attack Simulation Demo
M365 Attack Simulation Demo
Cheah Eng Soon
 
Cloud Security Demo
Cloud Security DemoCloud Security Demo
Cloud Security Demo
Cheah Eng Soon
 
Azure Active Directory - External Identities Demo
Azure Active Directory - External Identities Demo Azure Active Directory - External Identities Demo
Azure Active Directory - External Identities Demo
Cheah Eng Soon
 
Azure WAF
Azure WAFAzure WAF
Azure WAF
Cheah Eng Soon
 
Azure Weekend 2020 Build Malaysia Bus Uncle Chatbot
Azure Weekend 2020 Build Malaysia Bus Uncle ChatbotAzure Weekend 2020 Build Malaysia Bus Uncle Chatbot
Azure Weekend 2020 Build Malaysia Bus Uncle Chatbot
Cheah Eng Soon
 
Microsoft Azure的20大常见安全漏洞与配置错误
Microsoft Azure的20大常见安全漏洞与配置错误Microsoft Azure的20大常见安全漏洞与配置错误
Microsoft Azure的20大常见安全漏洞与配置错误
Cheah Eng Soon
 
20 common security vulnerabilities and misconfiguration in Azure
20 common security vulnerabilities and misconfiguration in Azure20 common security vulnerabilities and misconfiguration in Azure
20 common security vulnerabilities and misconfiguration in Azure
Cheah Eng Soon
 
Integrate Microsoft Graph with Azure Bot Services
Integrate Microsoft Graph with Azure Bot ServicesIntegrate Microsoft Graph with Azure Bot Services
Integrate Microsoft Graph with Azure Bot Services
Cheah Eng Soon
 
Azure Sentinel with Office 365
Azure Sentinel with Office 365Azure Sentinel with Office 365
Azure Sentinel with Office 365
Cheah Eng Soon
 

More from Cheah Eng Soon (20)

Microsoft Defender for Endpoint
Microsoft Defender for EndpointMicrosoft Defender for Endpoint
Microsoft Defender for Endpoint
 
Azure Active Directory - Secure and Govern
Azure Active Directory - Secure and GovernAzure Active Directory - Secure and Govern
Azure Active Directory - Secure and Govern
 
Microsoft Zero Trust
Microsoft Zero TrustMicrosoft Zero Trust
Microsoft Zero Trust
 
MEM for OnPrem Environments
MEM for OnPrem EnvironmentsMEM for OnPrem Environments
MEM for OnPrem Environments
 
Microsoft Threat Protection Automated Incident Response
Microsoft Threat Protection Automated Incident Response Microsoft Threat Protection Automated Incident Response
Microsoft Threat Protection Automated Incident Response
 
Azure Penetration Testing
Azure Penetration TestingAzure Penetration Testing
Azure Penetration Testing
 
Azure Penetration Testing
Azure Penetration TestingAzure Penetration Testing
Azure Penetration Testing
 
Penetration Testing Azure for Ethical Hackers
Penetration Testing Azure for Ethical HackersPenetration Testing Azure for Ethical Hackers
Penetration Testing Azure for Ethical Hackers
 
Microsoft Threat Protection Automated Incident Response Demo
Microsoft Threat Protection Automated Incident Response DemoMicrosoft Threat Protection Automated Incident Response Demo
Microsoft Threat Protection Automated Incident Response Demo
 
Microsoft Secure Score Demo
Microsoft Secure Score DemoMicrosoft Secure Score Demo
Microsoft Secure Score Demo
 
Microsoft Cloud App Security Demo
Microsoft Cloud App Security DemoMicrosoft Cloud App Security Demo
Microsoft Cloud App Security Demo
 
M365 Attack Simulation Demo
M365 Attack Simulation DemoM365 Attack Simulation Demo
M365 Attack Simulation Demo
 
Cloud Security Demo
Cloud Security DemoCloud Security Demo
Cloud Security Demo
 
Azure Active Directory - External Identities Demo
Azure Active Directory - External Identities Demo Azure Active Directory - External Identities Demo
Azure Active Directory - External Identities Demo
 
Azure WAF
Azure WAFAzure WAF
Azure WAF
 
Azure Weekend 2020 Build Malaysia Bus Uncle Chatbot
Azure Weekend 2020 Build Malaysia Bus Uncle ChatbotAzure Weekend 2020 Build Malaysia Bus Uncle Chatbot
Azure Weekend 2020 Build Malaysia Bus Uncle Chatbot
 
Microsoft Azure的20大常见安全漏洞与配置错误
Microsoft Azure的20大常见安全漏洞与配置错误Microsoft Azure的20大常见安全漏洞与配置错误
Microsoft Azure的20大常见安全漏洞与配置错误
 
20 common security vulnerabilities and misconfiguration in Azure
20 common security vulnerabilities and misconfiguration in Azure20 common security vulnerabilities and misconfiguration in Azure
20 common security vulnerabilities and misconfiguration in Azure
 
Integrate Microsoft Graph with Azure Bot Services
Integrate Microsoft Graph with Azure Bot ServicesIntegrate Microsoft Graph with Azure Bot Services
Integrate Microsoft Graph with Azure Bot Services
 
Azure Sentinel with Office 365
Azure Sentinel with Office 365Azure Sentinel with Office 365
Azure Sentinel with Office 365
 

Recently uploaded

20240607 QFM018 Elixir Reading List May 2024
20240607 QFM018 Elixir Reading List May 202420240607 QFM018 Elixir Reading List May 2024
20240607 QFM018 Elixir Reading List May 2024
Matthew Sinclair
 
Cosa hanno in comune un mattoncino Lego e la backdoor XZ?
Cosa hanno in comune un mattoncino Lego e la backdoor XZ?Cosa hanno in comune un mattoncino Lego e la backdoor XZ?
Cosa hanno in comune un mattoncino Lego e la backdoor XZ?
Speck&Tech
 
HCL Notes und Domino Lizenzkostenreduzierung in der Welt von DLAU
HCL Notes und Domino Lizenzkostenreduzierung in der Welt von DLAUHCL Notes und Domino Lizenzkostenreduzierung in der Welt von DLAU
HCL Notes und Domino Lizenzkostenreduzierung in der Welt von DLAU
panagenda
 
GraphSummit Singapore | Neo4j Product Vision & Roadmap - Q2 2024
GraphSummit Singapore | Neo4j Product Vision & Roadmap - Q2 2024GraphSummit Singapore | Neo4j Product Vision & Roadmap - Q2 2024
GraphSummit Singapore | Neo4j Product Vision & Roadmap - Q2 2024
Neo4j
 
Programming Foundation Models with DSPy - Meetup Slides
Programming Foundation Models with DSPy - Meetup SlidesProgramming Foundation Models with DSPy - Meetup Slides
Programming Foundation Models with DSPy - Meetup Slides
Zilliz
 
“Building and Scaling AI Applications with the Nx AI Manager,” a Presentation...
“Building and Scaling AI Applications with the Nx AI Manager,” a Presentation...“Building and Scaling AI Applications with the Nx AI Manager,” a Presentation...
“Building and Scaling AI Applications with the Nx AI Manager,” a Presentation...
Edge AI and Vision Alliance
 
GraphRAG for Life Science to increase LLM accuracy
GraphRAG for Life Science to increase LLM accuracyGraphRAG for Life Science to increase LLM accuracy
GraphRAG for Life Science to increase LLM accuracy
Tomaz Bratanic
 
RESUME BUILDER APPLICATION Project for students
RESUME BUILDER APPLICATION Project for studentsRESUME BUILDER APPLICATION Project for students
RESUME BUILDER APPLICATION Project for students
KAMESHS29
 
GraphSummit Singapore | The Art of the Possible with Graph - Q2 2024
GraphSummit Singapore | The Art of the  Possible with Graph - Q2 2024GraphSummit Singapore | The Art of the  Possible with Graph - Q2 2024
GraphSummit Singapore | The Art of the Possible with Graph - Q2 2024
Neo4j
 
How to use Firebase Data Connect For Flutter
How to use Firebase Data Connect For FlutterHow to use Firebase Data Connect For Flutter
How to use Firebase Data Connect For Flutter
Daiki Mogmet Ito
 
HCL Notes and Domino License Cost Reduction in the World of DLAU
HCL Notes and Domino License Cost Reduction in the World of DLAUHCL Notes and Domino License Cost Reduction in the World of DLAU
HCL Notes and Domino License Cost Reduction in the World of DLAU
panagenda
 
TrustArc Webinar - 2024 Global Privacy Survey
TrustArc Webinar - 2024 Global Privacy SurveyTrustArc Webinar - 2024 Global Privacy Survey
TrustArc Webinar - 2024 Global Privacy Survey
TrustArc
 
Introduction to CHERI technology - Cybersecurity
Introduction to CHERI technology - CybersecurityIntroduction to CHERI technology - Cybersecurity
Introduction to CHERI technology - Cybersecurity
mikeeftimakis1
 
GraphSummit Singapore | The Future of Agility: Supercharging Digital Transfor...
GraphSummit Singapore | The Future of Agility: Supercharging Digital Transfor...GraphSummit Singapore | The Future of Agility: Supercharging Digital Transfor...
GraphSummit Singapore | The Future of Agility: Supercharging Digital Transfor...
Neo4j
 
GraphSummit Singapore | Graphing Success: Revolutionising Organisational Stru...
GraphSummit Singapore | Graphing Success: Revolutionising Organisational Stru...GraphSummit Singapore | Graphing Success: Revolutionising Organisational Stru...
GraphSummit Singapore | Graphing Success: Revolutionising Organisational Stru...
Neo4j
 
みなさんこんにちはこれ何文字まで入るの?40文字以下不可とか本当に意味わからないけどこれ限界文字数書いてないからマジでやばい文字数いけるんじゃないの?えこ...
みなさんこんにちはこれ何文字まで入るの?40文字以下不可とか本当に意味わからないけどこれ限界文字数書いてないからマジでやばい文字数いけるんじゃないの?えこ...みなさんこんにちはこれ何文字まで入るの?40文字以下不可とか本当に意味わからないけどこれ限界文字数書いてないからマジでやばい文字数いけるんじゃないの?えこ...
みなさんこんにちはこれ何文字まで入るの?40文字以下不可とか本当に意味わからないけどこれ限界文字数書いてないからマジでやばい文字数いけるんじゃないの?えこ...
名前 です男
 
Video Streaming: Then, Now, and in the Future
Video Streaming: Then, Now, and in the FutureVideo Streaming: Then, Now, and in the Future
Video Streaming: Then, Now, and in the Future
Alpen-Adria-Universität
 
GenAI Pilot Implementation in the organizations
GenAI Pilot Implementation in the organizationsGenAI Pilot Implementation in the organizations
GenAI Pilot Implementation in the organizations
kumardaparthi1024
 
National Security Agency - NSA mobile device best practices
National Security Agency - NSA mobile device best practicesNational Security Agency - NSA mobile device best practices
National Security Agency - NSA mobile device best practices
Quotidiano Piemontese
 
Infrastructure Challenges in Scaling RAG with Custom AI models
Infrastructure Challenges in Scaling RAG with Custom AI modelsInfrastructure Challenges in Scaling RAG with Custom AI models
Infrastructure Challenges in Scaling RAG with Custom AI models
Zilliz
 

Recently uploaded (20)

20240607 QFM018 Elixir Reading List May 2024
20240607 QFM018 Elixir Reading List May 202420240607 QFM018 Elixir Reading List May 2024
20240607 QFM018 Elixir Reading List May 2024
 
Cosa hanno in comune un mattoncino Lego e la backdoor XZ?
Cosa hanno in comune un mattoncino Lego e la backdoor XZ?Cosa hanno in comune un mattoncino Lego e la backdoor XZ?
Cosa hanno in comune un mattoncino Lego e la backdoor XZ?
 
HCL Notes und Domino Lizenzkostenreduzierung in der Welt von DLAU
HCL Notes und Domino Lizenzkostenreduzierung in der Welt von DLAUHCL Notes und Domino Lizenzkostenreduzierung in der Welt von DLAU
HCL Notes und Domino Lizenzkostenreduzierung in der Welt von DLAU
 
GraphSummit Singapore | Neo4j Product Vision & Roadmap - Q2 2024
GraphSummit Singapore | Neo4j Product Vision & Roadmap - Q2 2024GraphSummit Singapore | Neo4j Product Vision & Roadmap - Q2 2024
GraphSummit Singapore | Neo4j Product Vision & Roadmap - Q2 2024
 
Programming Foundation Models with DSPy - Meetup Slides
Programming Foundation Models with DSPy - Meetup SlidesProgramming Foundation Models with DSPy - Meetup Slides
Programming Foundation Models with DSPy - Meetup Slides
 
“Building and Scaling AI Applications with the Nx AI Manager,” a Presentation...
“Building and Scaling AI Applications with the Nx AI Manager,” a Presentation...“Building and Scaling AI Applications with the Nx AI Manager,” a Presentation...
“Building and Scaling AI Applications with the Nx AI Manager,” a Presentation...
 
GraphRAG for Life Science to increase LLM accuracy
GraphRAG for Life Science to increase LLM accuracyGraphRAG for Life Science to increase LLM accuracy
GraphRAG for Life Science to increase LLM accuracy
 
RESUME BUILDER APPLICATION Project for students
RESUME BUILDER APPLICATION Project for studentsRESUME BUILDER APPLICATION Project for students
RESUME BUILDER APPLICATION Project for students
 
GraphSummit Singapore | The Art of the Possible with Graph - Q2 2024
GraphSummit Singapore | The Art of the  Possible with Graph - Q2 2024GraphSummit Singapore | The Art of the  Possible with Graph - Q2 2024
GraphSummit Singapore | The Art of the Possible with Graph - Q2 2024
 
How to use Firebase Data Connect For Flutter
How to use Firebase Data Connect For FlutterHow to use Firebase Data Connect For Flutter
How to use Firebase Data Connect For Flutter
 
HCL Notes and Domino License Cost Reduction in the World of DLAU
HCL Notes and Domino License Cost Reduction in the World of DLAUHCL Notes and Domino License Cost Reduction in the World of DLAU
HCL Notes and Domino License Cost Reduction in the World of DLAU
 
TrustArc Webinar - 2024 Global Privacy Survey
TrustArc Webinar - 2024 Global Privacy SurveyTrustArc Webinar - 2024 Global Privacy Survey
TrustArc Webinar - 2024 Global Privacy Survey
 
Introduction to CHERI technology - Cybersecurity
Introduction to CHERI technology - CybersecurityIntroduction to CHERI technology - Cybersecurity
Introduction to CHERI technology - Cybersecurity
 
GraphSummit Singapore | The Future of Agility: Supercharging Digital Transfor...
GraphSummit Singapore | The Future of Agility: Supercharging Digital Transfor...GraphSummit Singapore | The Future of Agility: Supercharging Digital Transfor...
GraphSummit Singapore | The Future of Agility: Supercharging Digital Transfor...
 
GraphSummit Singapore | Graphing Success: Revolutionising Organisational Stru...
GraphSummit Singapore | Graphing Success: Revolutionising Organisational Stru...GraphSummit Singapore | Graphing Success: Revolutionising Organisational Stru...
GraphSummit Singapore | Graphing Success: Revolutionising Organisational Stru...
 
みなさんこんにちはこれ何文字まで入るの?40文字以下不可とか本当に意味わからないけどこれ限界文字数書いてないからマジでやばい文字数いけるんじゃないの?えこ...
みなさんこんにちはこれ何文字まで入るの?40文字以下不可とか本当に意味わからないけどこれ限界文字数書いてないからマジでやばい文字数いけるんじゃないの?えこ...みなさんこんにちはこれ何文字まで入るの?40文字以下不可とか本当に意味わからないけどこれ限界文字数書いてないからマジでやばい文字数いけるんじゃないの?えこ...
みなさんこんにちはこれ何文字まで入るの?40文字以下不可とか本当に意味わからないけどこれ限界文字数書いてないからマジでやばい文字数いけるんじゃないの?えこ...
 
Video Streaming: Then, Now, and in the Future
Video Streaming: Then, Now, and in the FutureVideo Streaming: Then, Now, and in the Future
Video Streaming: Then, Now, and in the Future
 
GenAI Pilot Implementation in the organizations
GenAI Pilot Implementation in the organizationsGenAI Pilot Implementation in the organizations
GenAI Pilot Implementation in the organizations
 
National Security Agency - NSA mobile device best practices
National Security Agency - NSA mobile device best practicesNational Security Agency - NSA mobile device best practices
National Security Agency - NSA mobile device best practices
 
Infrastructure Challenges in Scaling RAG with Custom AI models
Infrastructure Challenges in Scaling RAG with Custom AI modelsInfrastructure Challenges in Scaling RAG with Custom AI models
Infrastructure Challenges in Scaling RAG with Custom AI models
 

Bot builder v4 HOL

  • 1. Bot Builder v4 HOL Chapter 1: Preparation • Visual Studio 2017 • .Net Core 2.x ( https://www.microsoft.com/net/download ) • Bot Builder V4 SDK Template for Visual Studio ( https://marketplace.visualstudio.com/items?itemName=BotBuilder.botb uilderv4 ) • Bot Emulator ( https://github.com/Microsoft/BotFramework- Emulator/releases ) • Ngrok (https://ngrok.com/ ) Or You can use Azure Bot Services . After Download the Ngrok, please launch your Bot Emulator and go to Emulator Settings and Setup as below.
  • 2. Chapter 2: Create Project 1. Launch your Visual Studio. 2. Create a New Project, File > New > Project 3. Select Multi-Project and Select Bot Builder Echo Bot v4 and Name your project, HandsonBot . Lastly , click OK button. 4. Make sure your bot template update to the v4.0.7
  • 3. 5. Test Run your project. Go to dropdownlist of IIS Express and Select HandsonBot , which is your project name and Press F5 for Debug your project. 6. After Debug, you will see the localhost as below. 7. Launch your Bot Framework Emulator and Click Open Bot and Select BotConfiguration.Bot that in your project.
  • 4. Chapter 3: Implementation of welcome message 1. Go to your project, right click > add new folder “SampleBot”. 2. After create the folder , right click > add class > name the class as “SampleBot”. 3. Implement the code as below. using Microsoft.Bot.Builder; using Microsoft.Bot.Builder.Dialogs; using Microsoft.Bot.Schema; using Microsoft.Extensions.Logging; using System; using System.Threading; using System.Threading.Tasks; namespace HandsonBot.SampleBot { public class SampleBot : IBot { private const string WelcomeText = "Welcome to Sample Bot"; private readonly ILogger _logger; private readonly SampleBotAccessors _accessors; // Added private readonly DialogSet _dialogs; // Added public SampleBot(SampleBotAccessors accessors, ILoggerFactory loggerFactory) // Updated { _accessors = accessors ?? throw new ArgumentException(nameof(accessors)); // Added _dialogs = new DialogSet(accessors.ConversationDialogState); // Added _dialogs.Add(new TextPrompt("name", ValidateHandleNameAsync)); _logger = loggerFactory.CreateLogger<SampleBot>(); _logger.LogInformation("Start SampleBot"); } private Task<bool> ValidateHandleNameAsync(PromptValidatorContext<string> promptContext, CancellationToken cancellationToken) { var result = promptContext.Recognized.Value; if (result != null && result.Length >= 3) { var upperValue = result.ToUpperInvariant(); promptContext.Recognized.Value = upperValue; return Task.FromResult(true); } return Task.FromResult(false); } public async Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken = default(CancellationToken)) { if (turnContext.Activity.Type == ActivityTypes.Message) { // We will exchange normal messages here.
  • 5. await SendMessageActivityAsync(turnContext, cancellationToken); // updated } else if (turnContext.Activity.Type == ActivityTypes.ConversationUpdate) { await SendWelcomeMessageAsync(turnContext, cancellationToken); } else { _logger.LogInformation($"passed:{turnContext.Activity.Type}"); } await _accessors.ConversationState.SaveChangesAsync(turnContext, false, cancellationToken); await _accessors.UserState.SaveChangesAsync(turnContext, false, cancellationToken); } private static async Task SendWelcomeMessageAsync(ITurnContext turnContext, CancellationToken cancellationToken) { foreach (var member in turnContext.Activity.MembersAdded) { if (member.Id != turnContext.Activity.Recipient.Id) { await turnContext.SendActivityAsync(WelcomeText, cancellationToken: cancellationToken); } } } private async Task SendMessageActivityAsync(ITurnContext turnContext, CancellationToken cancellationToken) { var dialogContext = await _dialogs.CreateContextAsync(turnContext, cancellationToken); var dialogTurnResult = await dialogContext.ContinueDialogAsync(cancellationToken); var userProfile = await _accessors.UserProfile.GetAsync(turnContext, () => new UserProfile(), cancellationToken); // If the handle name is not registered in UserState if (userProfile.HandleName == null) { await GetHandleNameAsync(dialogContext, dialogTurnResult, userProfile, cancellationToken); } else { await turnContext.SendActivityAsync($"Hello {userProfile.HandleName}", cancellationToken: cancellationToken); } } private async Task GetHandleNameAsync(DialogContext dialogContext, DialogTurnResult dialogTurnResult, UserProfile userProfile, CancellationToken cancellationToken) { if (dialogTurnResult.Status is DialogTurnStatus.Empty) {
  • 6. await dialogContext.PromptAsync( "name", new PromptOptions { Prompt = MessageFactory.Text("Please tell me your handle name first."), RetryPrompt = MessageFactory.Text("The handle name must be at least 3 words long."), }, cancellationToken); } else if (dialogTurnResult.Status is DialogTurnStatus.Complete) { // Register your handle name with UserState userProfile.HandleName = (string)dialogTurnResult.Result; _logger.LogInformation($"Handle Name registration: {userProfile.HandleName}"); } } } } 4. Go to Startup.cs Change near line 57 From services.AddBot<EchoWithCounterBot>(options => to services.AddBot<SampleBot.SampleBot>(options => & Change near line 78 From ILogger logger = _loggerFactory.CreateLogger<EchoWithCounterBot>(); to ILogger logger = _loggerFactory.CreateLogger<SampleBot.SampleBot>();
  • 7. Chapter 4: State Management 1. Right Click “SampleBot” Folder > right click > Add > Create UserProfile.class and Implement the code as below. namespace HandsonBot.SampleBot { public class UserProfile { public string HandleName { get; set; } } } 2. Install the Microsoft.Bot.Builder.Dialogs Nuget Package. Go to Tools> Nuget Package Manager> Package Manager Console and type the command as below. Install-Package Microsoft.Bot.Builder.Dialogs 3. Implement of State management Accessor Right Click “SampleBot” Folder > right click > Add > Create SampleBotAccessors.class and Implement the code as below. using System; using Microsoft.Bot.Builder; using Microsoft.Bot.Builder.Dialogs; namespace HandsonBot.SampleBot { public class SampleBotAccessors { public IStatePropertyAccessor<DialogState> ConversationDialogState { get; set; } public IStatePropertyAccessor<UserProfile> UserProfile { get; set; } public ConversationState ConversationState { get; } public UserState UserState { get; } public SampleBotAccessors(ConversationState conversationState, UserState userState) { ConversationState = conversationState ?? throw new ArgumentNullException(nameof(conversationState)); UserState = userState ?? throw new ArgumentException(nameof(userState)); } } }
  • 8. 4. Change Startup.cs near line 111, to implement Application of UserState Class From var conversationState = new ConversationState(dataStore); options.State.Add(conversationState); To var conversationState = new ConversationState(dataStore); options.State.Add(conversationState); var userState = new UserState(dataStore); options.State.Add(userState); 5. Implement of SampleBotAccessors class in Startup.cs , add the following code as below. using System; using System.Linq; using HandsonBot.SampleBot; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Bot.Builder; using Microsoft.Bot.Builder.Dialogs; using Microsoft.Bot.Builder.Integration; using Microsoft.Bot.Builder.Integration.AspNet.Core; using Microsoft.Bot.Configuration; using Microsoft.Bot.Connector.Authentication; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; namespace HandsonBot { /// <summary> /// The Startup class configures services and the request pipeline. /// </summary> public class Startup { private ILoggerFactory _loggerFactory; private bool _isProduction = false; public Startup(IHostingEnvironment env) { _isProduction = env.IsProduction(); var builder = new ConfigurationBuilder() .SetBasePath(env.ContentRootPath) .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) .AddJsonFile($"appsettings.{env.EnvironmentName}.json" , optional: true) .AddEnvironmentVariables();
  • 9. Configuration = builder.Build(); } /// <summary> /// Gets the configuration that represents a set of key/value application configuration properties. /// </summary> /// <value> /// The <see cref="IConfiguration"/> that represents a set of key/value application configuration properties. /// </value> public IConfiguration Configuration { get; } /// <summary> /// This method gets called by the runtime. Use this method to add services to the container. /// </summary> /// <param name="services">The <see cref="IServiceCollection"/> specifies the contract for a collection of service descriptors.</param> /// <seealso cref="IStatePropertyAccessor{T}"/> /// <seealso cref="https://docs.microsoft.com/en- us/aspnet/web-api/overview/advanced/dependency-injection"/> /// <seealso cref="https://docs.microsoft.com/en-us/azure/bot- service/bot-service-manage-channels?view=azure-bot-service-4.0"/> public void ConfigureServices(IServiceCollection services) { services.AddBot<SampleBot.SampleBot>(options => { var secretKey = Configuration.GetSection("botFileSecret")?.Value; var botFilePath = Configuration.GetSection("botFilePath")?.Value; // Loads .bot configuration file and adds a singleton that your Bot can access through dependency injection. var botConfig = BotConfiguration.Load(botFilePath ?? @".BotConfiguration.bot", secretKey); services.AddSingleton(sp => botConfig ?? throw new InvalidOperationException($"The .bot config file could not be loaded. ({botConfig})")); // Retrieve current endpoint. var environment = _isProduction ? "production" : "development"; var service = botConfig.Services.Where(s => s.Type == "endpoint" && s.Name == environment).FirstOrDefault(); if (!(service is EndpointService endpointService)) { throw new InvalidOperationException($"The .bot file does not contain an endpoint with name '{environment}'."); } options.CredentialProvider = new SimpleCredentialProvider(endpointService.AppId, endpointService.AppPassword); // Creates a logger for the application to use. ILogger logger = _loggerFactory.CreateLogger<SampleBot.SampleBot>();
  • 10. // Catches any errors that occur during a conversation turn and logs them. options.OnTurnError = async (context, exception) => { logger.LogError($"Exception caught : {exception}"); await context.SendActivityAsync("Sorry, it looks like something went wrong."); }; // The Memory Storage used here is for local bot debugging only. When the bot // is restarted, everything stored in memory will be gone. IStorage dataStore = new MemoryStorage(); // For production bots use the Azure Blob or // Azure CosmosDB storage providers. For 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/ // Uncomment the following lines to use Azure Blob Storage // //Storage configuration name or ID from the .bot file. // const string StorageConfigurationId = "<STORAGE- NAME-OR-ID-FROM-BOT-FILE>"; // var blobConfig = botConfig.FindServiceByNameOrId(StorageConfigurationId); // if (!(blobConfig is BlobStorageService blobStorageConfig)) // { // throw new InvalidOperationException($"The .bot file does not contain an blob storage with name '{StorageConfigurationId}'."); // } // // Default container name. // const string DefaultBotContainer = "<DEFAULT- CONTAINER>"; // var storageContainer = string.IsNullOrWhiteSpace(blobStorageConfig.Container) ? DefaultBotContainer : blobStorageConfig.Container; // IStorage dataStore = new Microsoft.Bot.Builder.Azure.AzureBlobStorage(blobStorageConfig.Connect ionString, storageContainer); // Create Conversation State object. // The Conversation State object is where we persist anything at the conversation-scope. var conversationState = new ConversationState(dataStore); options.State.Add(conversationState); var userState = new UserState(dataStore); options.State.Add(userState); });
  • 11. // Create and register state accesssors. // Acessors created here are passed into the IBot-derived class on every turn. services.AddSingleton<EchoBotAccessors>(sp => { var options = sp.GetRequiredService<IOptions<BotFrameworkOptions>>().Value; if (options == null) { throw new InvalidOperationException("BotFrameworkOptions must be configured prior to setting up the state accessors"); } var conversationState = options.State.OfType<ConversationState>().FirstOrDefault(); if (conversationState == null) { throw new InvalidOperationException("ConversationState must be defined and added before adding conversation-scoped state accessors."); } // Create the custom state accessor. // State accessors enable other components to read and write individual properties of state. var accessors = new EchoBotAccessors(conversationState) { CounterState = conversationState.CreateProperty<CounterState>(EchoBotAccessors.Counte rStateName), }; return accessors; }); // Create and register state accesssors. // Acessors created here are passed into the IBot-derived class on every turn. services.AddSingleton<SampleBotAccessors>(sp => { var options = sp.GetRequiredService<IOptions<BotFrameworkOptions>>().Value ?? throw new InvalidOperationException("BotFrameworkOptions must be configured prior to setting up the state accessors"); var conversationState = options.State.OfType<ConversationState>().FirstOrDefault() ?? throw new InvalidOperationException("ConversationState が ConfigureServices で設 定されていません。"); var userState = options.State.OfType<UserState>().FirstOrDefault()
  • 12. ?? throw new InvalidOperationException("UserState が ConfigureServices で設定されてい ません。"); var accessors = new SampleBotAccessors(conversationState, userState) { ConversationDialogState = conversationState.CreateProperty<DialogState>(nameof(DialogState)), UserProfile = userState.CreateProperty<UserProfile>(nameof(UserProfile)), }; return accessors; }); } public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { _loggerFactory = loggerFactory; app.UseDefaultFiles() .UseStaticFiles() .UseBotFramework(); } } } 6. Go back to SampleBot.cs added the following code with comment and Waterfallsteps. //Added: Part where code is added //Updated: Changing parts
  • 13. using Microsoft.Bot.Builder; using Microsoft.Bot.Builder.Dialogs; using Microsoft.Bot.Schema; using Microsoft.Extensions.Logging; using System; using System.Threading; using System.Threading.Tasks; namespace HandsonBot.SampleBot { public class SampleBot : IBot { private const string WelcomeText = "Welcome to Sample Bot"; private readonly ILogger _logger; private readonly SampleBotAccessors _accessors; // Added private readonly DialogSet _dialogs; // Added public SampleBot(SampleBotAccessors accessors, ILoggerFactory loggerFactory) { _accessors = accessors ?? throw new ArgumentException(nameof(accessors)); _dialogs = new DialogSet(accessors.ConversationDialogState); var waterfallSteps = new WaterfallStep[] { ConfirmAgeStepAsync, ExecuteAgeStepAsync, ExecuteFinalConfirmStepAsync, ExecuteSummaryStepAsync, }; _dialogs.Add(new TextPrompt("name", ValidateHandleNameAsync)); _dialogs.Add(new ConfirmPrompt("confirm")); _dialogs.Add(new NumberPrompt<int>("age")); _dialogs.Add(new WaterfallDialog("details", waterfallSteps)); _logger = loggerFactory.CreateLogger<SampleBot>(); _logger.LogInformation("Start SampleBot"); } private async Task GetHandleNameAsync(DialogContext dialogContext, DialogTurnResult dialogTurnResult, UserProfile userProfile, CancellationToken cancellationToken) { if (dialogTurnResult.Status is DialogTurnStatus.Empty) { await dialogContext.PromptAsync( "name", new PromptOptions { Prompt = MessageFactory.Text("Please tell me your handle name first."), RetryPrompt = MessageFactory.Text("The handle name must be at least 3 words long."), }, cancellationToken);
  • 14. } // If you enter a handle name else if (dialogTurnResult.Status is DialogTurnStatus.Complete) { if (dialogTurnResult.Result != null) { // Register your handle name with UserState userProfile.HandleName = (string)dialogTurnResult.Result; await dialogContext.BeginDialogAsync("details", null, cancellationToken); // added } } } private Task<bool> ValidateHandleNameAsync(PromptValidatorContext<string> promptContext, CancellationToken cancellationToken) { var result = promptContext.Recognized.Value; if (result != null && result.Length >= 3) { var upperValue = result.ToUpperInvariant(); promptContext.Recognized.Value = upperValue; return Task.FromResult(true); } return Task.FromResult(false); } public async Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken = default(CancellationToken)) { if (turnContext.Activity.Type == ActivityTypes.Message) { // We will exchange normal messages here. await SendMessageActivityAsync(turnContext, cancellationToken); // updated } else if (turnContext.Activity.Type == ActivityTypes.ConversationUpdate) { await SendWelcomeMessageAsync(turnContext, cancellationToken); } else { _logger.LogInformation($"passed:{turnContext.Activity.Type}"); } await _accessors.ConversationState.SaveChangesAsync(turnContext, false, cancellationToken); await _accessors.UserState.SaveChangesAsync(turnContext, false, cancellationToken); }
  • 15. private static async Task SendWelcomeMessageAsync(ITurnContext turnContext, CancellationToken cancellationToken) { foreach (var member in turnContext.Activity.MembersAdded) { if (member.Id != turnContext.Activity.Recipient.Id) { await turnContext.SendActivityAsync(WelcomeText, cancellationToken: cancellationToken); } } } public async Task SendMessageActivityAsync(ITurnContext turnContext, CancellationToken cancellationToken) { var dialogContext = await _dialogs.CreateContextAsync(turnContext, cancellationToken); var dialogTurnResult = await dialogContext.ContinueDialogAsync(cancellationToken); var userProfile = await _accessors.UserProfile.GetAsync(turnContext, () => new UserProfile(), cancellationToken); // If the handle name is not registered in UserState if (userProfile.HandleName == null) { await GetHandleNameAsync(dialogContext, dialogTurnResult, userProfile, cancellationToken); } // If you have a handle name registered with UserState else { // added if (dialogTurnResult.Status == DialogTurnStatus.Empty) { await dialogContext.BeginDialogAsync("details", null, cancellationToken); } } } //---------------------------------------------- //Lab private async Task<DialogTurnResult> ConfirmAgeStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken) { var userProfile = await _accessors.UserProfile.GetAsync(stepContext.Context, () => new UserProfile(), cancellationToken); return await stepContext.PromptAsync( "confirm", new PromptOptions { Prompt = MessageFactory.Text($"{userProfile.HandleName} May I ask your age?"),
  • 16. RetryPrompt = MessageFactory.Text("Answer yes or No."), }, cancellationToken); } private async Task<DialogTurnResult> ExecuteAgeStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken) { if ((bool)stepContext.Result) { return await stepContext.PromptAsync( "age", new PromptOptions { Prompt = MessageFactory.Text("What is your age?"), RetryPrompt = MessageFactory.Text("Enter the age in numbers."), }, cancellationToken); } else { return await stepContext.NextAsync(-1, cancellationToken); } } private async Task<DialogTurnResult> ExecuteFinalConfirmStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken) { var userProfile = await _accessors.UserProfile.GetAsync(stepContext.Context, () => new UserProfile(), cancellationToken); userProfile.Age = (int)stepContext.Result; var message = GetAgeAcceptedMessage(userProfile); await stepContext.Context.SendActivityAsync(message, cancellationToken); return await stepContext.PromptAsync( "confirm", new PromptOptions { Prompt = MessageFactory.Text("Is this the registration information you want?") }, cancellationToken); } private static IActivity GetAgeAcceptedMessage(UserProfile userProfile) { return MessageFactory.Text(userProfile.Age == -1 ? "Age is private, isn't it?" : $"I'm {userProfile.Age} year old."); } private async Task<DialogTurnResult> ExecuteSummaryStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken) { if ((bool)stepContext.Result) {
  • 17. var userProfile = await _accessors.UserProfile.GetAsync(stepContext.Context, () => new UserProfile(), cancellationToken); var summaryMessages = GetSummaryMessages(userProfile); await stepContext.Context.SendActivitiesAsync(summaryMessages, cancellationToken); // End of Detail dialog return await stepContext.EndDialogAsync(cancellationToken: cancellationToken); } else { // Redo the Details dialog. await stepContext.Context.SendActivityAsync(MessageFactory.Text("I will visit you again."), cancellationToken); return await stepContext.ReplaceDialogAsync("details", cancellationToken: cancellationToken); } } private static IActivity[] GetSummaryMessages(UserProfile userProfile) { IActivity summaryMessage = MessageFactory.Text(userProfile.Age == -1 ? $"{userProfile.HandleName} Your age is private." : $"{userProfile.HandleName} , {userProfile.Age} year old."); IActivity thanksMessage = MessageFactory.Text("Thank you for your input."); return new[] { summaryMessage, thanksMessage }; } //--------------------------- } }
  • 18. Chapter 5: Implementation Age Property 1. Go to UserProfile.cs , implement Age Property as shown below. namespace HandsonBot.SampleBot { public class UserProfile { public string HandleName { get; set; } public int Age { get; set; } } } 2. Debug your application again. Happy Coding !!!