Reputation: 5853
I'm trying to inherit the PromptString class and loop the prompt until the user gives a correct input.
I have written the following code to validate if the user specifies a valid date or not.
This is my Custom Prompt class, where I want to validate if user enters a correct date or not, if he enters a correct date, it should simply exit the dialog, else it should re-prompt for the input again.
namespace CustomPromptDemo.Models
{
public class CustomPrompt : PromptString
{
public CustomPrompt(string Prompt = "Please enter a date", string Retry = "Please enter a valid date!!!", int RetryCount = 3) : base(Prompt, Retry, RetryCount)
{
}
protected override IMessageActivity MakePrompt(IDialogContext context, string prompt, IReadOnlyList<string> options = null, IReadOnlyList<string> descriptions = null, string speak = null)
{
return base.MakePrompt(context, prompt, options, descriptions, speak);
}
bool ValidateIfDateOrNot(string Query)
{
try
{
DateTime date = default(DateTime);
DateTimeModel model = DateTimeRecognizer.GetInstance().GetDateTimeModel(Culture.English);
List<ModelResult> parsedResults = model.Parse(Query);
var resolvedValue = (parsedResults.SelectMany(x => x.Resolution).FirstOrDefault().Value as List<Dictionary<string, string>>).SelectMany(x => x).Where(x => x.Key.Equals("value")).Select(y => y.Value).FirstOrDefault();
return DateTime.TryParse(resolvedValue, out date);
}
catch (Exception ex)
{
throw;
}
}
protected override async Task MessageReceivedAsync(IDialogContext context, IAwaitable<IMessageActivity> message)
{
IMessageActivity msg = await message;
if (ValidateIfDateOrNot(msg.Text))
{
await context.PostAsync("Valid date!");
context.Done<object>(null);
}
else
{
await context.PostAsync("Please enter a valid date!");
await base.MessageReceivedAsync(context, message);
}
}
protected override bool TryParse(IMessageActivity message, out string result)
{
return base.TryParse(message, out result);
}
}
}
This is my RootDialog
namespace CustomPromptDemo.Dialogs
{
[Serializable]
public class RootDialog : IDialog<object>
{
public Task StartAsync(IDialogContext context)
{
context.Wait(MessageReceivedAsync);
return Task.CompletedTask;
}
private async Task MessageReceivedAsync(IDialogContext context, IAwaitable<object> result)
{
CustomPrompt c = new CustomPrompt();
c.
}
}
}
I am having a multitude of methods to choose from like in the below screenshot.
I am having trouble invoking the custom prompt I made. Any help is appreciated, thanks.
Upvotes: 0
Views: 1098
Reputation: 3539
You can make use of the DateTimePrompt or if you really want more control a simple TextPrompt and provide a custom validator for it:
AddDialog(new DateTimePrompt(nameof(DateTimePrompt), DateTimePromptValidator));
Within your dialog you could kick off the prompt providing a retry prompt for a separate message to be shown when the retry fails:
return await stepcontext.PromptAsync(nameof(DateTimePrompt), new PromptOptions
{
Prompt = MessageFactory.Text("Please enter the date."),
RetryPrompt = MessageFactory.Text("Please enter a valid date")
}, cancellationtoken);
If you want to handle advanced cases with your dates like multiple languages I suggest taking a look at Recognizers-Text from Microsoft which currently power pre-build entities in LUIS: Language Understanding Intelligent Service.
Upvotes: 1
Reputation: 4329
I have built something very similar in my bot that prompts the user for an image and then continues looping if they don't input a valid image:
[Serializable]
public class PhotoInputDialog : IDialog<string>
{
public ICustomInputValidator<IMessageActivity> Validator { get; private set; }
public string InputPrompt { get; private set; }
public string WrongInputPrompt { get; private set; }
public static PhotoInputDialog Create
(string inputPrompt, string wrongInputPrompt, ICustomInputValidator<IMessageActivity> validator)
{
return new PhotoInputDialog()
{ InputPrompt = inputPrompt, WrongInputPrompt = wrongInputPrompt, Validator = validator };
}
public async Task StartAsync(IDialogContext context)
{
await context.PostAsync(InputPrompt);
context.Wait(InputGiven);
}
private async Task InputGiven(IDialogContext context, IAwaitable<IMessageActivity> result)
{
var message = await result;
if (!Validator.IsValid(message))
{
await context.PostAsync(WrongInputPrompt);
context.Wait(InputGiven);
}
else
context.Done(message.Attachments.First().ContentUrl);
}
}
How it works:
When I create the dialog I submit a validator class that I use to validate the result, here's one I created to verify text input on a certain length. It's not 100% necessary but it is nice and reusable now:
[Serializable()]
public class TextCustomInputValidator : ICustomInputValidator<string>
{
private int MinLength, MaxLength;
public TextCustomInputValidator(int minLength, int maxLength)
{
MinLength = minLength;
MaxLength = maxLength;
}
public bool IsValid(string input)
{
return input.Length >= MinLength && input.Length <= MaxLength;
}
}
There are a multitude of different ways to do a Bot in the Bot Framework so there are probably other ways it is possible. Another that springs to mind is using FormFlow as that has a date input validator: https://docs.botframework.com/en-us/csharp/builder/sdkreference/dc/db8/class_microsoft_1_1_bot_1_1_builder_1_1_form_flow_1_1_advanced_1_1_recognize_date_time.html
Upvotes: 1
Reputation: 14609
You can use it just like PromptDialog.Text is using it, see on GitHub sources:
public class PromptDialog
{
public static void Text(IDialogContext context, ResumeAfter<string> resume, string prompt, string retry = null, int attempts = 3)
{
var child = new PromptString(prompt, retry, attempts);
context.Call<string>(child, resume);
}
So in your case:
[Serializable]
public class RootDialog : IDialog<object>
{
public Task StartAsync(IDialogContext context)
{
context.Wait(MessageReceivedAsync);
return Task.CompletedTask;
}
private async Task MessageReceivedAsync(IDialogContext context, IAwaitable<object> result)
{
var prompt = new CustomPrompt();
context.Call<string>(prompt, ResumeAfterPromptString);
}
private async Task ResumeAfterPromptString(IDialogContext context, IAwaitable<string> result)
{
// Do what you want here... But your customPrompt should return a string
context.Done<object>(null);
}
}
By the way, you also have to make your CustomPrompt
serializable:
[Serializable]
public class CustomPrompt : PromptDialog.PromptString
And you should return your string instead of null if ValidateIfDateOrNot
is ok.
EDIT: last thing, you should have a look to the sample provided on GitHub's project about the recognizer you are using, here , which is a good example of the parsing of a date just like you want
Upvotes: 1