Microsoft Bot Framework中的FormBuilder的自定义字段

问题描述:

使用 AlarmBot示例

Using the AlarmBot Sample and the Improved Sandwich Bot, I'm trying to understand how to combine the FormBuilder with custom dialog behavior. Specifically, I want to take the Alarm Bot SetAlarm method

SetAlarm(IDialogContext context, LuisResult result)

,并在SandwichBot中将相同的Chronic.Parser用作DeliveryTime字段.怎么办?我已经看过实现IField了,但是我不确定它是什么样子.

and leverage the same Chronic.Parser in the SandwichBot for the DeliveryTime field. How would that be done? I've looked at implementing IField but I'm not sure what it would look like.

在查看并逐步查看源代码之后,最简单的答案与IFieldState无关.关键界面是IRecognize.正确使用它需要了解一些幕后操作.

After looking and stepping through the source, the easiest answer isn't about the IFieldState. The key interface is IRecognize. Using it correctly requires understanding a little bit of what's going on under the covers.

首先创建您自己的自定义字段.该框架很好地允许我们从执行大多数工作的FieldReflector派生.

Start by creating your own custom field. The framework nicely allows us the derive from the FieldReflector which does most of the work.

public class BetterDateTimeField : FieldReflector<MyOrder> 
{
  public BetterDateTimeField(string name, bool ignoreAnnotations = false) 
          : base(name, ignoreAnnotations)  { }

   public override IForm<MyOrder> Form
   {
      set
      {
        base.Form = value;
        base.SetRecognizer(new BetterDateTimeRecognizer<MyOrder>(this, CultureInfo.CurrentCulture));
      }
    }
}

这里的主要思想是创建自己的识别器,因为那是获取原始输入文本的部分.诀窍是要知道何时可以创建识别器的实例.必须在之后字段中设置表格.底层的Recognizer基类将查看构造函数中的字段表单. (如果您完全放弃使用基本的Recognizer类,那么这可能不是问题.)

The main idea here is to create your own recognizer since that's the part that gets the raw input text. The trick is to know when you can create an instance of the recognizer. It has to be done AFTER the field Form has been set. The underlying Recognizer base class will look at the fields form in the constructor. (If you completely abandon using the base Recognizer classes this may not be an issue.)

接下来,您可以创建自己的自定义IRecognize实现.不幸的是,该bot框架密封了许多基本/原始类型识别器类,因此从RecognizeDateTime派生而重载Parse则不是一个选择(希望有朝一日它们会解封它们).但是,将其复制并编辑到您自己的自定义类中很容易.

Next you can create your own custom IRecognize implementation. Unfortunately, the bot framework seals many of the basic/primitive type recognizer classes so deriving from RecognizeDateTime and overloading Parse isn't an option (hopefully someday they'll unseal them). However, it's easy enough to copy and edit into your own custom class.

using Chronic;
public class BetterDateTimeRecognizer<T> : RecognizePrimitive<T> where T : class
{
   private CultureInfo _culture;
   private Parser _parser;

   public BetterDateTimeRecognizer(IField<T> field, CultureInfo culture) 
          : base(field)
   {
      _culture = culture;
      _parser = new Chronic.Parser();
   }

   public override string Help(T state, object defaultValue)
   {
     var prompt = new Prompter<T>(_field.Template(TemplateUsage.DateTimeHelp), _field.Form, null);
     var args = HelpArgs(state, defaultValue);
     return prompt.Prompt(state, _field.Name, args.ToArray());

   }

   public override TermMatch Parse(string input)
   {
     TermMatch match = null;
     // the original code
     //DateTime dt;
     //if (DateTime.TryParse(input, out dt))
     //{
     //    match = new TermMatch(0, input.Length, 1.0, dt);
     //}

     var parse = _parser.Parse(input);
     if (parse != null && parse.Start.HasValue)
     {
         match = new TermMatch(0, input.Length, 1.0, parse.Start.Value);
     }

     return match;
  }

  public override IEnumerable<string> ValidInputs(object value)
  {
     yield return ValueDescription(value);
  }

  public override string ValueDescription(object value)
  {
     return ((DateTime)value).ToString(CultureInfo.CurrentCulture.DateTimeFormat);
  }
}

最后,您只需要在BuildForm()中将其连接到表单构建器即可:

Lastly, you just have to wire it up to your form builder in BuildForm():

var form = builder.Message("Hello there. What can I help you today")
           .Field(new BetterDateTimeField("<NAME of YOUR DateTime Field HERE>")
           .Build();