Пользовательские поля с FormBuilder в Microsoft Bot Framework
Используя пример AlarmBot и улучшенный сэндвич-бот, я пытаюсь понять, как объединить FormBuilder с пользовательским поведением диалога. В частности, я хочу использовать метод Alarm Bot SetAlarm
SetAlarm(IDialogContext context, LuisResult result)
и использовать тот же Chronic.Parser в SandwichBot для поля DeliveryTime. Как это будет сделано? Я смотрел на реализацию IField, но я не уверен, как это будет выглядеть.
2 ответа
После просмотра и просмотра исходного кода, самый простой ответ не о IFieldState. Ключевым интерфейсом является IRecognize. Правильное использование требует понимания того, что происходит под одеялом.
Начните с создания собственного настраиваемого поля. Фреймворк позволяет нам получить производные от FieldReflector, который выполняет большую часть работы.
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, это может не быть проблемой.)
Затем вы можете создать свою собственную реализацию IRecognize. К сожалению, среда ботов герметизирует многие классы распознавателя базовых / примитивных типов, поэтому наследование от RecognizeDateTime и перегрузка Parse не являются приемлемыми (надеюсь, когда-нибудь они их распечатают). Тем не менее, это достаточно легко скопировать и отредактировать в свой собственный класс.
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():
var form = builder.Message("Hello there. What can I help you today")
.Field(new BetterDateTimeField("<NAME of YOUR DateTime Field HERE>")
.Build();
Первоначально мы использовали Chronic для полей DateTime в FormFlow, но когда мы перешли к подписанию нашей dll, мы больше не могли использовать неподписанную библиотеку C# Chronic. Я обратился к автору, хотя и попросил его подписать его, но пока не получил ответа - если он это сделает, мы сделаем это частью FormFlow.
Что касается реализации IField, если вы хотите, вы можете использовать Field и предоставить собственную реализацию IFieldState. Поле имеет структуры данных для всех декларативных вещей, и вы можете переопределить все методы. Это позволит вам использовать Chronic в вашей неподписанной DLL.