forked from microsoft/BotBuilder-Samples
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathPromptValidationsBot.cs
158 lines (141 loc) · 8.33 KB
/
PromptValidationsBot.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Schema;
namespace Microsoft.BotBuilderSamples
{
/// <summary>
/// Represents a bot that processes incoming activities.
/// For each user interaction, an instance of this class is created and the OnTurnAsync method is called.
/// This is a Transient lifetime service. Transient lifetime services are created
/// each time they're requested. For each Activity received, a new instance of this
/// class is created. Objects that are expensive to construct, or have a lifetime
/// beyond the single turn, should be carefully managed.
/// For example, the <see cref="MemoryStorage"/> object and associated
/// <see cref="IStatePropertyAccessor{T}"/> object are created with a singleton lifetime.
/// </summary>
/// <seealso cref="https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-2.1"/>
public class PromptValidationsBot : IBot
{
private const string WelcomeText = "This bot will introduce you to prompt validations. Type anything to get started";
private readonly BotAccessors _accessors;
/// <summary>
/// The <see cref="DialogSet"/> that contains all the Dialogs that can be used at runtime.
/// </summary>
private readonly DialogSet _dialogs;
/// <summary>
/// Initializes a new instance of the <see cref="PromptValidationsBot"/> class.
/// </summary>
/// <param name="accessors">The state accessors this instance will be needing at runtime.</param>
public PromptValidationsBot(BotAccessors accessors)
{
_accessors = accessors ?? throw new ArgumentNullException(nameof(accessors));
_dialogs = new DialogSet(accessors.ConversationDialogState);
_dialogs.Add(new TextPrompt("name", CustomPromptValidatorAsync));
}
/// <summary>
/// This controls what happens when an <see cref="Activity"/> gets sent to the bot.
/// </summary>
/// <param name="turnContext">Provides the <see cref="ITurnContext"/> for the turn of the bot.</param>
/// <param name="cancellationToken" >(Optional) A <see cref="CancellationToken"/> that can be used by other objects
/// or threads to receive notice of cancellation.</param>
/// <returns>>A <see cref="Task"/> representing the operation result of the Turn operation.</returns>
public async Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken = default(CancellationToken))
{
if (turnContext == null)
{
throw new ArgumentNullException(nameof(turnContext));
}
// We are only interested in Message Activities.
if (turnContext.Activity.Type == ActivityTypes.Message)
{
// Run the DialogSet - let the framework identify the current state of the dialog from
// the dialog stack and figure out what (if any) is the active dialog.
var dialogContext = await _dialogs.CreateContextAsync(turnContext, cancellationToken);
var results = await dialogContext.ContinueDialogAsync(cancellationToken);
// If the DialogTurnStatus is Empty we should start a new dialog.
if (results.Status == DialogTurnStatus.Empty)
{
// A prompt dialog can be started directly on from the DialogContext. The prompt text is given in the PromptOptions.
// We have defined a RetryPrompt here so this will be used. Otherwise the Prompt text will be repeated.
await dialogContext.PromptAsync(
"name",
new PromptOptions
{
Prompt = MessageFactory.Text("Please enter a name."),
RetryPrompt = MessageFactory.Text("A name must be more than three characters in length. Please try again."),
},
cancellationToken);
}
// We had a dialog run (it was the prompt) now it is Complete.
else if (results.Status == DialogTurnStatus.Complete)
{
// Check for a result.
if (results.Result != null)
{
// And finish by sending a message to the user. Next time ContinueAsync is called it will return DialogTurnStatus.Empty.
await turnContext.SendActivityAsync(MessageFactory.Text($"Thank you, I have your name as '{results.Result}'."), cancellationToken);
}
}
}
else if (turnContext.Activity.Type == ActivityTypes.ConversationUpdate)
{
// Send a welcome message to the user and tell them what actions they may perform to use this bot
await SendWelcomeMessageAsync(turnContext, cancellationToken);
}
else
{
await turnContext.SendActivityAsync($"{turnContext.Activity.Type} event detected", cancellationToken: cancellationToken);
}
// Save the new turn count into the conversation state.
await _accessors.ConversationState.SaveChangesAsync(turnContext, false, cancellationToken);
}
/// <summary>
/// This is an example of a custom validator. This example can be directly used on a float NumberPrompt.
/// Returning true indicates the recognized value is acceptable. Returning false will trigger re-prompt behavior.
/// </summary>
/// <param name="promptContext">The <see cref="PromptValidatorContext"/> gives the validator code access to the runtime, including the recognized value and the turn context.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>A <see cref="Task"/> representing the operation result of the Turn operation.</returns>
public Task<bool> CustomPromptValidatorAsync(PromptValidatorContext<string> promptContext, CancellationToken cancellationToken)
{
var result = promptContext.Recognized.Value;
// This condition is our validation rule.
if (result != null && result.Length > 3)
{
// You are free to change the value you have collected. By way of illustration we are simply uppercasing.
var newValue = result.ToUpperInvariant();
promptContext.Recognized.Value = newValue;
// Success is indicated by passing back the value the Prompt has collected. You must pass back a value even if you haven't changed it.
return Task.FromResult(true);
}
// Not calling End indicates validation failure. This will trigger a RetryPrompt if one has been defined.
// Note you are free to do async IO from within a validator. Here we had no need so just complete.
return Task.FromResult(false);
}
/// <summary>
/// On a conversation update activity sent to the bot, the bot will
/// send a message to the any new user(s) that were added.
/// </summary>
/// <param name="turnContext">Provides the <see cref="ITurnContext"/> for the turn of the bot.</param>
/// <param name="cancellationToken" >(Optional) A <see cref="CancellationToken"/> that can be used by other objects
/// or threads to receive notice of cancellation.</param>
/// <returns>>A <see cref="Task"/> representing the operation result of the Turn operation.</returns>
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(
$"Welcome to PromptValidationBot {member.Name}. {WelcomeText}",
cancellationToken: cancellationToken);
}
}
}
}
}