public async Task MessageReceived()

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;
                }
            }
        }