in CSharp/Library/Microsoft.Bot.Builder/FormFlow/FormDialog.cs [269:509]
public async Task MessageReceived(IDialogContext context, IAwaitable<Connector.IMessageActivity> toBot)
{
try
{
var message = toBot == null ? null : await toBot;
// Ensure we have initial definition for field steps
foreach (var step in _form.Steps)
{
if (step.Type == StepType.Field && step.Field.Prompt == null)
{
await step.DefineAsync(_state);
}
}
var next = (_formState.Next == null ? new NextStep() : ActiveSteps(_formState.Next, _state));
bool waitForMessage = false;
FormPrompt lastPrompt = _formState.LastPrompt;
Func<FormPrompt, IStep<T>, Task<FormPrompt>> PostAsync = async (prompt, step) =>
{
return await _form.Prompt(context, prompt, _state, step.Field);
};
Func<IStep<T>, IEnumerable<TermMatch>, Task<bool>> DoStepAsync = async (step, matches) =>
{
var result = await step.ProcessAsync(context, _state, _formState, message, matches);
await SkipSteps();
next = result.Next;
if (result.Feedback?.Prompt != null)
{
await PostAsync(result.Feedback, step);
if (_formState.Phase() != StepPhase.Completed)
{
if (!_formState.ProcessInputs)
{
await PostAsync(lastPrompt, step);
waitForMessage = true;
}
else if (result.Prompt?.Buttons != null)
{
// We showed buttons so allow them to be pressed
waitForMessage = true;
}
else
{
// After simple feedback, reset to ready
_formState.SetPhase(StepPhase.Ready);
}
}
}
if (result.Prompt != null)
{
lastPrompt = await PostAsync(result.Prompt, step);
waitForMessage = true;
}
return true;
};
while (!waitForMessage && MoveToNext(next))
{
IStep<T> step = null;
IEnumerable<TermMatch> matches = null;
if (next.Direction == StepDirection.Named && next.Names.Length > 1)
{
// We need to choose between multiple next steps
bool start = (_formState.Next == null);
_formState.Next = next;
step = new NavigationStep<T>(_form.Steps[_formState.Step].Name, _form, _state, _formState);
if (start)
{
lastPrompt = await PostAsync(step.Start(context, _state, _formState), step);
waitForMessage = true;
}
else
{
// Responding
matches = step.Match(context, _state, _formState, message);
}
}
else
{
// Processing current step
step = _form.Steps[_formState.Step];
if (await step.DefineAsync(_state))
{
if (_formState.Phase() == StepPhase.Ready)
{
if (step.Type == StepType.Message)
{
await PostAsync(step.Start(context, _state, _formState), step);
next = new NextStep();
}
else if (_formState.ProcessInputs)
{
message = MessageActivityHelper.BuildMessageWithText(_formState.FieldInputs.Last().Item2);
lastPrompt = step.Start(context, _state, _formState);
}
else
{
lastPrompt = await PostAsync(step.Start(context, _state, _formState), step);
waitForMessage = true;
}
}
else if (_formState.Phase() == StepPhase.Responding)
{
matches = step.Match(context, _state, _formState, message);
}
}
else
{
_formState.SetPhase(StepPhase.Completed);
lastPrompt = null;
next = new NextStep(StepDirection.Next);
}
}
if (matches != null)
{
var inputText = MessageActivityHelper.GetSanitizedTextInput(message);
matches = MatchAnalyzer.Coalesce(matches, inputText).ToArray();
if (MatchAnalyzer.IsFullMatch(inputText, matches))
{
await DoStepAsync(step, matches);
}
else
{
// Filter non-active steps out of command matches
var messageText = message.Text;
var commands =
(messageText == null || messageText.Trim().StartsWith("\""))
? new TermMatch[0]
: (from command in MatchAnalyzer.Coalesce(_commands.Prompt.Recognizer.Matches(message), messageText)
where (command.Value is FormCommand
|| (!_formState.ProcessInputs && _form.Fields.Field((string)command.Value).Active(_state)))
select command).ToArray();
if (commands.Length == 1 && MatchAnalyzer.IsFullMatch(messageText, commands))
{
FormPrompt feedback;
next = DoCommand(context, _state, _formState, step, commands, out feedback);
if (feedback != null)
{
await PostAsync(feedback, step);
await PostAsync(lastPrompt, step);
waitForMessage = true;
}
}
else
{
if (matches.Count() == 0 && commands.Count() == 0)
{
await PostAsync(step.NotUnderstood(context, _state, _formState, message), step);
if (_formState.ProcessInputs && !step.InClarify(_formState))
{
_formState.SetPhase(StepPhase.Ready);
}
else
{
waitForMessage = true;
}
}
else
{
// Go with response since it looks possible
var bestMatch = MatchAnalyzer.BestMatches(matches, commands);
if (bestMatch == 0)
{
await DoStepAsync(step, matches);
}
else
{
FormPrompt feedback;
next = DoCommand(context, _state, _formState, step, commands, out feedback);
if (feedback != null)
{
await PostAsync(feedback, step);
await PostAsync(lastPrompt, step);
waitForMessage = true;
}
}
}
}
}
}
next = ActiveSteps(next, _state);
}
if (next.Direction == StepDirection.Complete || next.Direction == StepDirection.Quit)
{
if (next.Direction == StepDirection.Complete)
{
if (_form.Completion != null)
{
await _form.Completion(context, _state);
}
context.Done(_state);
}
else if (next.Direction == StepDirection.Quit)
{
throw new FormCanceledException<T>("Form quit.")
{
LastForm = _state,
Last = _form.Steps[_formState.Step].Name,
Completed = (from step in _form.Steps
where _formState.Phase(_form.StepIndex(step)) == StepPhase.Completed
select step.Name).ToArray()
};
}
else
{
throw new NotImplementedException();
}
}
else
{
_formState.LastPrompt = (FormPrompt)lastPrompt?.Clone();
context.Wait(MessageReceived);
}
}
catch (Exception inner)
{
if (!(inner is FormCanceledException<T>))
{
throw new FormCanceledException<T>(inner.Message, inner)
{
LastForm = _state,
Last = _form.Steps[_formState.Step].Name,
Completed = (from step in _form.Steps
where _formState.Phase(_form.StepIndex(step)) == StepPhase.Completed
select step.Name).ToArray()
};
}
else
{
throw;
}
}
}