FormDialog.cs
1 //
2 // Copyright (c) Microsoft. All rights reserved.
3 // Licensed under the MIT license.
4 //
5 // Microsoft Bot Framework: http://botframework.com
6 //
7 // Bot Builder SDK GitHub:
8 // https://github.com/Microsoft/BotBuilder
9 //
10 // Copyright (c) Microsoft Corporation
11 // All rights reserved.
12 //
13 // MIT License:
14 // Permission is hereby granted, free of charge, to any person obtaining
15 // a copy of this software and associated documentation files (the
16 // "Software"), to deal in the Software without restriction, including
17 // without limitation the rights to use, copy, modify, merge, publish,
18 // distribute, sublicense, and/or sell copies of the Software, and to
19 // permit persons to whom the Software is furnished to do so, subject to
20 // the following conditions:
21 //
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
24 //
25 // THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND,
26 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 //
33 
34 using System;
35 using System.Collections.Generic;
36 using System.Globalization;
37 using System.Linq;
38 using System.Runtime.Serialization;
39 using System.Text;
40 using System.Threading.Tasks;
41 
48 using Microsoft.Bot.Connector;
49 
50 namespace Microsoft.Bot.Builder.FormFlow
51 {
55  public static class FormDialog
56  {
63  public static IFormDialog<T> FromType<T>(FormOptions options = FormOptions.None) where T : class, new()
64  {
65  return new FormDialog<T>(new T(), null, options);
66  }
67 
75  public static IFormDialog<T> FromForm<T>(BuildFormDelegate<T> buildForm, FormOptions options = FormOptions.None) where T : class, new()
76  {
77  return new FormDialog<T>(new T(), buildForm, options);
78  }
79 
80 
81  #region IForm<T> statics
82 #if DEBUG
83  internal static bool DebugRecognizers = false;
84 #endif
85  #endregion
86  }
87 
91  [Flags]
92  public enum FormOptions
93  {
97  None,
98 
103 
108  };
109 
117  public delegate IForm<T> BuildFormDelegate<T>() where T : class;
118 
128  [Serializable]
129  public sealed class FormDialog<T> : IFormDialog<T>, ISerializable
130  where T : class
131  {
132  // constructor arguments
133  private readonly T _state;
134  private readonly BuildFormDelegate<T> _buildForm;
135  private readonly IEnumerable<EntityRecommendation> _entities;
136  private readonly FormOptions _options;
137 
138  // instantiated in constructor, saved when serialized
139  private readonly FormState _formState;
140 
141  // instantiated in constructor, re-instantiated when deserialized
142  private readonly IForm<T> _form;
143  private readonly IField<T> _commands;
144 
145  private static IForm<T> BuildDefaultForm()
146  {
147  return new FormBuilder<T>().AddRemainingFields().Build();
148  }
149 
150  #region Documentation
151  #endregion
159  public FormDialog(T state, BuildFormDelegate<T> buildForm = null, FormOptions options = FormOptions.None, IEnumerable<EntityRecommendation> entities = null, CultureInfo cultureInfo = null)
160  {
161  buildForm = buildForm ?? BuildDefaultForm;
162  entities = entities ?? Enumerable.Empty<EntityRecommendation>();
163  if (cultureInfo != null)
164  {
165  CultureInfo.CurrentUICulture = cultureInfo;
166  CultureInfo.CurrentCulture = cultureInfo;
167  }
168 
169  // constructor arguments
170  SetField.NotNull(out this._state, nameof(state), state);
171  SetField.NotNull(out this._buildForm, nameof(buildForm), buildForm);
172  SetField.NotNull(out this._entities, nameof(entities), entities);
173  this._options = options;
174 
175  // make our form
176  var form = _buildForm();
177 
178  // instantiated in constructor, saved when serialized
179  this._formState = new FormState(form.Steps.Count);
180 
181  // instantiated in constructor, re-instantiated when deserialized
182  this._form = form;
183  this._commands = this._form.BuildCommandRecognizer();
184  }
185 
186  private FormDialog(SerializationInfo info, StreamingContext context)
187  {
188  // constructor arguments
189  SetField.NotNullFrom(out this._state, nameof(this._state), info);
190  SetField.NotNullFrom(out this._buildForm, nameof(this._buildForm), info);
191  SetField.NotNullFrom(out this._entities, nameof(this._entities), info);
192  this._options = info.GetValue<FormOptions>(nameof(this._options));
193 
194  // instantiated in constructor, saved when serialized
195  SetField.NotNullFrom(out this._formState, nameof(this._formState), info);
196 
197  // instantiated in constructor, re-instantiated when deserialized
198  this._form = _buildForm();
199  this._commands = this._form.BuildCommandRecognizer();
200  }
201 
202  void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
203  {
204  // constructor arguments
205  info.AddValue(nameof(this._state), this._state);
206  info.AddValue(nameof(this._buildForm), this._buildForm);
207  info.AddValue(nameof(this._entities), this._entities);
208  info.AddValue(nameof(this._options), this._options);
209 
210  // instantiated in constructor, saved when serialized
211  info.AddValue(nameof(this._formState), this._formState);
212  }
213 
214  #region IFormDialog<T> implementation
215 
216  IForm<T> IFormDialog<T>.Form { get { return this._form; } }
217 
218  #endregion
219 
220  #region IDialog implementation
221 
222  async Task IDialog<T>.StartAsync(IDialogContext context)
223  {
224  if (this._entities.Any())
225  {
226  var inputs = new List<Tuple<int, string>>();
227  var entityGroups = (from entity in this._entities group entity by entity.Role ?? entity.Type);
228  foreach (var entityGroup in entityGroups)
229  {
230  var step = _form.Step(entityGroup.Key);
231  if (step != null)
232  {
233  var builder = new StringBuilder();
234  var first = true;
235  foreach (var entity in entityGroup)
236  {
237  if (first)
238  {
239  first = false;
240  }
241  else
242  {
243  builder.Append(' ');
244  }
245  builder.Append(entity.Entity);
246  }
247  inputs.Add(Tuple.Create(_form.StepIndex(step), builder.ToString()));
248  }
249  }
250  if (inputs.Any())
251  {
252  // Descending because last entry is first processed
253  _formState.FieldInputs = (from input in inputs orderby input.Item1 descending select input).ToList();
254  }
255  }
256  await SkipSteps();
257  _formState.Step = 0;
258  _formState.StepState = null;
259 
260  if (this._options.HasFlag(FormOptions.PromptInStart))
261  {
262  await MessageReceived(context, null);
263  }
264  else
265  {
266  context.Wait(MessageReceived);
267  }
268  }
269 
271  {
272  try
273  {
274  var toBotText = (toBot != null ? (await toBot).Text : null);
275  var stepInput = toBotText == null ? "" : toBotText.Trim();
276  if (stepInput.StartsWith("\""))
277  {
278  stepInput = stepInput.Substring(1);
279  }
280  if (stepInput.EndsWith("\""))
281  {
282  stepInput = stepInput.Substring(0, stepInput.Length - 1);
283  }
284  // Ensure we have initial definition for field steps
285  foreach (var step in _form.Steps)
286  {
287  if (step.Type == StepType.Field && step.Field.Prompt == null)
288  {
289  await step.DefineAsync(_state);
290  }
291  }
292  var next = (_formState.Next == null ? new NextStep() : ActiveSteps(_formState.Next, _state));
293  bool waitForMessage = false;
294  FormPrompt lastPrompt = _formState.LastPrompt;
295  Func<FormPrompt, IStep<T>, Task<FormPrompt>> PostAsync = async (prompt, step) =>
296  {
297  return await _form.Prompt(context, prompt, _state, step.Field);
298  };
299  Func<IStep<T>, IEnumerable<TermMatch>, Task<bool>> DoStepAsync = async (step, matches) =>
300  {
301  var result = await step.ProcessAsync(context, _state, _formState, stepInput, matches);
302  await SkipSteps();
303  next = result.Next;
304  if (result.Feedback?.Prompt != null)
305  {
306  await PostAsync(result.Feedback, step);
307  if (_formState.Phase() != StepPhase.Completed)
308  {
309  if (!_formState.ProcessInputs)
310  {
311  await PostAsync(lastPrompt, step);
312  waitForMessage = true;
313  }
314  else if (result.Prompt?.Buttons != null)
315  {
316  // We showed buttons so allow them to be pressed
317  waitForMessage = true;
318  }
319  else
320  {
321  // After simple feedback, reset to ready
322  _formState.SetPhase(StepPhase.Ready);
323  }
324  }
325  }
326  if (result.Prompt != null)
327  {
328  lastPrompt = await PostAsync(result.Prompt, step);
329  waitForMessage = true;
330  }
331  return true;
332  };
333  while (!waitForMessage && MoveToNext(next))
334  {
335  IStep<T> step = null;
336  IEnumerable<TermMatch> matches = null;
337  if (next.Direction == StepDirection.Named && next.Names.Length > 1)
338  {
339  // We need to choose between multiple next steps
340  bool start = (_formState.Next == null);
341  _formState.Next = next;
342  step = new NavigationStep<T>(_form.Steps[_formState.Step].Name, _form, _state, _formState);
343  if (start)
344  {
345  lastPrompt = await PostAsync(step.Start(context, _state, _formState), step);
346  waitForMessage = true;
347  }
348  else
349  {
350  // Responding
351  matches = step.Match(context, _state, _formState, stepInput);
352  }
353  }
354  else
355  {
356  // Processing current step
357  step = _form.Steps[_formState.Step];
358  if (await step.DefineAsync(_state))
359  {
360  if (_formState.Phase() == StepPhase.Ready)
361  {
362  if (step.Type == StepType.Message)
363  {
364  await PostAsync(step.Start(context, _state, _formState), step);
365  next = new NextStep();
366  }
367  else if (_formState.ProcessInputs)
368  {
369  stepInput = _formState.FieldInputs.Last().Item2;
370  lastPrompt = step.Start(context, _state, _formState);
371  }
372  else
373  {
374  lastPrompt = await PostAsync(step.Start(context, _state, _formState), step);
375  waitForMessage = true;
376  }
377  }
378  else if (_formState.Phase() == StepPhase.Responding)
379  {
380  matches = step.Match(context, _state, _formState, stepInput);
381  }
382  }
383  else
384  {
385  _formState.SetPhase(StepPhase.Completed);
386  lastPrompt = null;
387  next = new NextStep(StepDirection.Next);
388  }
389  }
390  if (matches != null)
391  {
392  matches = MatchAnalyzer.Coalesce(matches, stepInput).ToArray();
393  if (MatchAnalyzer.IsFullMatch(stepInput, matches))
394  {
395  await DoStepAsync(step, matches);
396  }
397  else
398  {
399  // Filter non-active steps out of command matches
400  var commands =
401  (toBotText == null || toBotText.Trim().StartsWith("\""))
402  ? new TermMatch[0]
403  : (from command in MatchAnalyzer.Coalesce(_commands.Prompt.Recognizer.Matches(toBotText), toBotText)
404  where (command.Value is FormCommand
405  || (!_formState.ProcessInputs && _form.Fields.Field((string)command.Value).Active(_state)))
406  select command).ToArray();
407  if (MatchAnalyzer.IsFullMatch(toBotText, commands))
408  {
409  FormPrompt feedback;
410  next = DoCommand(context, _state, _formState, step, commands, out feedback);
411  if (feedback != null)
412  {
413  await PostAsync(feedback, step);
414  await PostAsync(lastPrompt, step);
415  waitForMessage = true;
416  }
417  }
418  else
419  {
420  if (matches.Count() == 0 && commands.Count() == 0)
421  {
422  await PostAsync(step.NotUnderstood(context, _state, _formState, stepInput), step);
423  if (_formState.ProcessInputs && !step.InClarify(_formState))
424  {
425  _formState.SetPhase(StepPhase.Ready);
426  }
427  else
428  {
429  waitForMessage = true;
430  }
431  }
432  else
433  {
434  // Go with response since it looks possible
435  var bestMatch = MatchAnalyzer.BestMatches(matches, commands);
436  if (bestMatch == 0)
437  {
438  await DoStepAsync(step, matches);
439  }
440  else
441  {
442  FormPrompt feedback;
443  next = DoCommand(context, _state, _formState, step, commands, out feedback);
444  if (feedback != null)
445  {
446  await PostAsync(feedback, step);
447  await PostAsync(lastPrompt, step);
448  waitForMessage = true;
449  }
450  }
451  }
452  }
453  }
454  }
455  next = ActiveSteps(next, _state);
456  }
457  if (next.Direction == StepDirection.Complete || next.Direction == StepDirection.Quit)
458  {
459  if (next.Direction == StepDirection.Complete)
460  {
461  if (_form.Completion != null)
462  {
463  await _form.Completion(context, _state);
464  }
465  context.Done(_state);
466  }
467  else if (next.Direction == StepDirection.Quit)
468  {
469  throw new FormCanceledException<T>("Form quit.")
470  {
471  LastForm = _state,
472  Last = _form.Steps[_formState.Step].Name,
473  Completed = (from step in _form.Steps
474  where _formState.Phase(_form.StepIndex(step)) == StepPhase.Completed
475  select step.Name).ToArray()
476  };
477  }
478  else
479  {
480  throw new NotImplementedException();
481  }
482  }
483  else
484  {
485  _formState.LastPrompt = (FormPrompt)lastPrompt?.Clone();
486  context.Wait(MessageReceived);
487  }
488  }
489  catch (Exception inner)
490  {
491  if (!(inner is FormCanceledException<T>))
492  {
493  throw new FormCanceledException<T>(inner.Message, inner)
494  {
495  LastForm = _state,
496  Last = _form.Steps[_formState.Step].Name,
497  Completed = (from step in _form.Steps
498  where _formState.Phase(_form.StepIndex(step)) == StepPhase.Completed
499  select step.Name).ToArray()
500  };
501  }
502  else
503  {
504  throw;
505  }
506  }
507  }
508 
509  #endregion
510 
511  #region Implementation
512 
513  private async Task SkipSteps()
514  {
515  if (!_options.HasFlag(FormOptions.PromptFieldsWithValues))
516  {
517  // Skip steps that already have a value if they are nullable and valid.
518  foreach (var step in _form.Steps)
519  {
520  int stepi = _form.StepIndex(step);
521  if (step.Type == StepType.Field
522  && _formState.Phase(stepi) == StepPhase.Ready
523  && !step.Field.IsUnknown(_state)
524  && step.Field.IsNullable)
525  {
526  var defined = await step.DefineAsync(_state);
527  if (defined)
528  {
529  var val = step.Field.GetValue(_state);
530  var result = await step.Field.ValidateAsync(_state, val);
531  if (result.IsValid)
532  {
533  bool ok = true;
534  double min, max;
535  if (step.Field.Limits(out min, out max))
536  {
537  var num = (double)Convert.ChangeType(val, typeof(double));
538  ok = (num >= min && num <= max);
539  }
540  if (ok)
541  {
542  _formState.SetPhase(stepi, StepPhase.Completed);
543  }
544  }
545  }
546  }
547  }
548  }
549  }
550 
551  private NextStep ActiveSteps(NextStep next, T state)
552  {
553  var result = next;
554  if (next.Direction == StepDirection.Named)
555  {
556  var names = (from name in next.Names where _form.Fields.Field(name).Active(state) select name);
557  var count = names.Count();
558  if (count == 0)
559  {
560  result = new NextStep();
561  }
562  else if (count != result.Names.Length)
563  {
564  result = new NextStep(names);
565  }
566  }
567  return result;
568  }
569 
575  private bool MoveToNext(NextStep next)
576  {
577  bool found = false;
578  switch (next.Direction)
579  {
580  case StepDirection.Complete:
581  break;
582  case StepDirection.Named:
583  _formState.StepState = null;
584  if (next.Names.Length == 0)
585  {
586  goto case StepDirection.Next;
587  }
588  else if (next.Names.Length == 1)
589  {
590  var name = next.Names.First();
591  var nextStep = -1;
592  for (var i = 0; i < _form.Steps.Count(); ++i)
593  {
594  if (_form.Steps[i].Name == name)
595  {
596  nextStep = i;
597  break;
598  }
599  }
600  if (nextStep == -1)
601  {
602  throw new ArgumentOutOfRangeException("NextStep", "Does not correspond to a field in the form.");
603  }
604  if (_form.Steps[nextStep].Active(_state))
605  {
606  var current = _form.Steps[_formState.Step];
607  _formState.SetPhase(_form.Fields.Field(current.Name).IsUnknown(_state) ? StepPhase.Ready : StepPhase.Completed);
608  _formState.History.Push(_formState.Step);
609  _formState.Step = nextStep;
610  _formState.SetPhase(StepPhase.Ready);
611  found = true;
612  }
613  else
614  {
615  // If we went to a state which is not active fall through to the next active if any
616  goto case StepDirection.Next;
617  }
618  }
619  else
620  {
621  // Always mark multiple names as found so we can handle the user navigation
622  found = true;
623  }
624  break;
625  case StepDirection.Next:
626  {
627  var start = _formState.Step;
628  // Reset any non-optional field step that has been reset to no value
629  for (var i = 0; i < _form.Steps.Count; ++i)
630  {
631  var step = _form.Steps[i];
632  if (step.Type == StepType.Field && _formState.Phase(i) == StepPhase.Completed && !step.Field.Optional && step.Field.IsUnknown(_state))
633  {
634  _formState.SetPhase(i, StepPhase.Ready);
635  }
636  }
637  // Next ready step including current one
638  for (var offset = 0; offset < _form.Steps.Count; ++offset)
639  {
640  var istep = (start + offset) % _form.Steps.Count;
641  var step = _form.Steps[istep];
642  _formState.Step = istep;
643  if (offset > 0)
644  {
645  _formState.StepState = null;
646  _formState.Next = null;
647  }
648  if ((_formState.Phase(istep) == StepPhase.Ready || _formState.Phase(istep) == StepPhase.Responding)
649  && step.Active(_state))
650  {
651  // Ensure all dependencies have values
652  foreach (var dependency in step.Dependencies)
653  {
654  var dstep = _form.Step(dependency);
655  var dstepi = _form.StepIndex(dstep);
656  if (dstep.Active(_state) && _formState.Phases[dstepi] != StepPhase.Completed)
657  {
658  _formState.Step = dstepi;
659  break;
660  }
661  }
662  found = true;
663  break;
664  }
665  }
666  if (!found)
667  {
668  next.Direction = StepDirection.Complete;
669  }
670  else
671  {
672  var normalStep = _formState.Step;
673  // Process initial messages first, then FieldInputs
674  if ((_formState.ProcessInputs || _form.Steps[normalStep].Type != StepType.Message) && _formState.FieldInputs != null)
675  {
676  // Override normal choice with FieldInputs
677  Func<bool> NextFieldInput = () =>
678  {
679  var foundInput = false;
680  while (_formState.FieldInputs.Any() && !foundInput)
681  {
682  var possible = _formState.FieldInputs.Last().Item1;
683  if (_form.Steps[possible].Active(_state))
684  {
685  _formState.Step = possible;
686  foundInput = true;
687  }
688  else
689  {
690  _formState.FieldInputs.Pop();
691  }
692  }
693  if (!_formState.FieldInputs.Any())
694  {
695  if (_options.HasFlag(FormOptions.PromptFieldsWithValues))
696  {
697  _formState.Reset();
698  }
699  else
700  {
701  _formState.ProcessInputs = false;
702  _formState.FieldInputs = null;
703  _formState.Step = 0;
704  }
705  // Skip initial messages since we showed them already
706  while (_formState.Step < _form.Steps.Count() && _form.Steps[_formState.Step].Type == StepType.Message)
707  {
708  _formState.SetPhase(StepPhase.Completed);
709  ++_formState.Step;
710  }
711  }
712  return foundInput;
713  };
714  if (!_formState.ProcessInputs)
715  {
716  // Start of processing inputs
717  _formState.ProcessInputs = NextFieldInput();
718  }
719  else if (_formState.Phase(start) == StepPhase.Completed || _formState.Phase(start) == StepPhase.Ready)
720  {
721  // Reset state of just completed step
722  if (_options.HasFlag(FormOptions.PromptFieldsWithValues))
723  {
724  _formState.SetPhase(StepPhase.Ready);
725  }
726  // Move on to next field input if any
727  _formState.FieldInputs.Pop();
728  NextFieldInput();
729  }
730  }
731  else
732  {
733  if (_formState.Step != start && _form.Steps[start].Type != StepType.Message)
734  {
735  _formState.History.Push(start);
736  }
737  }
738  }
739  }
740  break;
741  case StepDirection.Previous:
742  while (_formState.History.Count() > 0)
743  {
744  var lastStepIndex = _formState.History.Pop();
745  var lastStep = _form.Steps[lastStepIndex];
746  if (lastStep.Active(_state))
747  {
748  var step = _form.Steps[_formState.Step];
749  _formState.SetPhase(step.Field.IsUnknown(_state) ? StepPhase.Ready : StepPhase.Completed);
750  _formState.Step = lastStepIndex;
751  _formState.SetPhase(StepPhase.Ready);
752  _formState.StepState = null;
753  _formState.Next = null;
754  found = true;
755  break;
756  }
757  }
758  if (!found)
759  {
760  next.Direction = StepDirection.Quit;
761  }
762  break;
763  case StepDirection.Quit:
764  break;
765  case StepDirection.Reset:
766  _formState.Reset();
767  // Because we redo phase they can go through everything again but with defaults.
768  found = true;
769  break;
770  }
771  return found;
772  }
773 
774  private NextStep DoCommand(IDialogContext context, T state, FormState form, IStep<T> step, IEnumerable<TermMatch> matches, out FormPrompt feedback)
775  {
776  // TODO: What if there are more than one command?
777  feedback = null;
778  var next = new NextStep();
779  var value = matches.First().Value;
780  if (value is FormCommand)
781  {
782  switch ((FormCommand)value)
783  {
784  case FormCommand.Backup:
785  {
786  next.Direction = step.Back(context, state, form) ? StepDirection.Next : StepDirection.Previous;
787  }
788  break;
789  case FormCommand.Help:
790  {
791  var field = step.Field;
792  var builder = new StringBuilder();
793  foreach (var entry in _form.Configuration.Commands)
794  {
795  builder.Append("* ");
796  builder.AppendLine(entry.Value.Help);
797  }
798  var navigation = new Prompter<T>(field.Template(TemplateUsage.NavigationCommandHelp), _form, null);
799  var active = (from istep in _form.Steps
800  where !form.ProcessInputs && istep.Type == StepType.Field && istep.Active(state)
801  select istep.Field.FieldDescription.Description).ToArray();
802  if (active.Length > 1)
803  {
804  var activeList = Language.BuildList(active, navigation.Annotation.ChoiceSeparator, navigation.Annotation.ChoiceLastSeparator);
805  builder.Append("* ");
806  builder.Append(navigation.Prompt(state, null, activeList));
807  }
808  feedback = step.Help(state, form, builder.ToString());
809  }
810  break;
811  case FormCommand.Quit: next.Direction = StepDirection.Quit; break;
812  case FormCommand.Reset: next.Direction = StepDirection.Reset; break;
813  case FormCommand.Status:
814  {
815  var prompt = new PromptAttribute("{*}");
816  feedback = new Prompter<T>(prompt, _form, null).Prompt(state, null);
817  }
818  break;
819  }
820  }
821  else
822  {
823  var name = (string)value;
824  var istep = _form.Step(name);
825  if (istep != null && istep.Active(state))
826  {
827  next = new NextStep(new string[] { name });
828  }
829  }
830  return next;
831  }
832 
833  #endregion
834  }
835 }
836 
837 namespace Microsoft.Bot.Builder.Luis.Models
838 {
839  [Serializable]
840  public partial class EntityRecommendation
841  {
842  }
843 
844  [Serializable]
845  public partial class IntentRecommendation
846  {
847  }
848 }
849 
850 
Task StartAsync(IDialogContext context)
The start of the code that represents the conversational dialog.
Core namespace for FormFlow and associated infrastructure.
Definition: Attributes.cs:39
The context for the execution of a dialog&#39;s conversational process.
async Task MessageReceived(IDialogContext context, IAwaitable< Connector.IMessageActivity > toBot)
Definition: FormDialog.cs:270
Namespace for the Microsoft Bot Connector SDK.
A prompt and recognizer packaged together.
Definition: IPrompt.cs:330
StepDirection
Direction for next step.
Definition: IField.cs:248
Namespace for models generated from the http://luis.ai REST API.
Definition: FormDialog.cs:837
Define the prompt used when asking about a field.
Definition: Attributes.cs:294
IForm< T > Form
The form specification.
Definition: IFormDialog.cs:140
Explicit interface to support the compiling of async/await.
Definition: Awaitable.cs:55
Build a form by specifying messages, fields and confirmations via reflection or programatically.
Definition: FormBuilder.cs:387
TemplateUsage
All of the built-in templates.
Definition: Attributes.cs:321
Do not show possible choices in the prompt
string[] Names
If this is a named step, one or more named steps to move to. If there are more than one...
Definition: IField.cs:322
Interface for all the information about a specific field.
Definition: IField.cs:404
StepDirection Direction
Direction for next step.
Definition: IField.cs:317
Prompt for fields that already have a value in the initial state when processing form.
string ChoiceSeparator
When constructing inline lists using {||} in a Pattern Language string, the string used between all c...
Definition: Attributes.cs:710
Namespace for internal machinery that is not useful for most developers and may change in the future...
string Prompt
The text prompt that corresponds to Message.Text.
Definition: IPrompt.cs:91
Namespace for FormFlow advanced building blocks.
Definition: Attributes.cs:672
Form definition interface.
Definition: IForm.cs:47
static string BuildList(IEnumerable< string > values, string separator, string lastSeparator)
Given a list of string values generate a proper English list.
Definition: Language.cs:283
FormOptions
Options for form execution.
Definition: FormDialog.cs:92
Interface for controlling a FormFlow dialog.
Definition: IFormDialog.cs:134
LUIS intent recommendation. Look at https://www.luis.ai/Help for more information.
Definition: FormDialog.cs:845
FormDialog(T state, BuildFormDelegate< T > buildForm=null, FormOptions options=FormOptions.None, IEnumerable< EntityRecommendation > entities=null, CultureInfo cultureInfo=null)
Constructor for creating a FormFlow dialog.
Definition: FormDialog.cs:159
Namespace for internal Dialogs machinery that is not useful for most developers and may change in the...
Exception generated when form filling is canceled by user quit or exception.
Definition: IFormDialog.cs:214
FormCommand
Commands supported in form dialogs.
Definition: IFormDialog.cs:146
Luis entity recommendation. Look at https://www.luis.ai/Help for more information.
Definition: FormDialog.cs:840
Static factory methods for creating form dialogs.
Definition: FormDialog.cs:55
Namespace for the internal fibers machinery that is not useful for most developers and may change in ...
Core namespace for Dialogs and associated infrastructure.
Namespace for the machinery needed to talk to http://luis.ai.
Describe a possible match in the user input.
Definition: IRecognize.cs:53
override IForm< T > Build(Assembly resourceAssembly=null, string resourceName=null)
Build the form based on the methods called on the builder.
Definition: FormBuilder.cs:402
Root namespace for the Microsoft Bot Builder SDK.
The prompt that is returned by form prompter.
Definition: IPrompt.cs:85