3

我正在使用机器人框架,并且正在尝试使用 FormFlow 动态定义表单。我对一个特定领域有疑问:

.Field(new FieldReflector<IssueFormModel>(nameof(IssueResultModel.ProductPlatform))
                .SetType(typeof(string))
                .SetDefine(async (issue, field) =>
                {
                    if (issue.ProductName != null)
                    {
                        foreach (var platform in productsWithPlatforms[issue.ProductName])
                        {
                            field.AddDescription(platform, platform).AddTerms(platform, platform);
                        }
                    }

                    return await Task.FromResult(true);
                })
                .SetPrompt(new PromptAttribute("Which platform of {ProductName}{||}?"))
                .SetAllowsMultiple(false)
                .SetValidate(async (state, value) => await ValidateProductPlatform(value, state)))

问题是ProductPlatform依赖于ProductName,因此它是一个字符串。这很好用,但是,通过这种方式,机器人不会显示可能平台的选项(尽管 {||} 在SetPrompt中有)。

当我将 type 设置为 nullSetType(null)时,机器人现在将可能的平台显示为按钮,但是,当用户决定键入错误的平台而不是单击正确的平台时,它永远不会进入ValidateProductPlatform(我猜验证本身已经在SetDefine级别完成)。我需要通过ValidateProductPlatform验证用户输入的唯一原因是我想在 3 次尝试失败后取消表单。

那么,有什么方法可以实现这一点?:用户有基于 ProductName 的 ProductPlatorm 选项(作为按钮),但是他们没有单击,而是(可能)输入了错误的平台,并且在 3 次错误尝试后,表单结束。

PS:我看到了Microsoft Bot : How to capture Too Many Attempts in Form Flow?但我无法使用它,因为在我的情况下似乎 SetValidate被忽略了(当SetType(null)

4

1 回答 1

0

创建同时使用按钮和自定义验证方法(在文本输入和按钮按下时)的提示器的一种技术是在表单上插入提示器覆盖。对于您的特定用例,这将类似于以下代码块(此实现覆盖 ProductPlatform 字段上的原始提示,以便向卡片添加按钮菜单):

form.Prompter(async (context, prompt, state, field) =>
{
    //this part is added in on top of the original implementation of form.Prompter
    if (field.Name == nameof(IssueFormModel.ProductPlatform) && prompt.Buttons.Count == 0)
    {
        foreach (var fieldValue in field.Values)
        {
            var asString = fieldValue as string;
            prompt.Buttons.Add(new DescribeAttribute(asString, title: asString));
        }
    }
    //this check prevents prompts that are sent through from ValidateResult without a FeedbackCard from crashing the bot.
    if (prompt.Description != null) {
        var preamble = context.MakeMessage();
        var promptMessage = context.MakeMessage();
        if (prompt.GenerateMessages(preamble, promptMessage))
        {
            await context.PostAsync(preamble);
        }
        await context.PostAsync(promptMessage);
    }
    return prompt;
});

这意味着您构建表单的整个方法应该如下所示:

private IForm<IssueFormModel> BuildProductForm()
{
    var form = new FormBuilder<IssueFormModel>()
       .Field(new FieldReflector<IssueFormModel>(nameof(IssueFormModel.ProductName))
             .SetPrompt(new PromptAttribute("Type either product1 or product2"))
             .SetValidate(async (state, value) => await ValidateProductName(value, state)))
       .Field(new FieldReflector<IssueFormModel>(nameof(IssueFormModel.ProductPlatform))
             .SetType(typeof(string))
             .SetDefine(async (issue, field) =>
             {
                 if (issue.ProductName != null)
                 {
                     foreach (var platform in productsWithPlatforms[issue.ProductName])
                     {
                         field.AddDescription(platform, platform).AddTerms(platform, platform);
                     }
                 }

                 return await Task.FromResult(true);
             })
             .SetPrompt(new PromptAttribute("Which platform of {ProductName}{||}?"))
             .SetAllowsMultiple(false)
             .SetValidate(async (state, value) => await ValidateProductPlatform(value, state)))
       .AddRemainingFields()
       .Confirm(prompt: "Is this your issue? {*}{||}");

    form.Prompter(async (context, prompt, state, field) =>
    {
        if (field.Name == nameof(IssueFormModel.ProductPlatform) && prompt.Buttons.Count == 0)
        {
            foreach (var fieldValue in field.Values)
            {
                var asString = fieldValue as string;
                prompt.Buttons.Add(new DescribeAttribute(asString, title: asString));
            }
        }
        if (prompt.Description != null) {
            var preamble = context.MakeMessage();
            var promptMessage = context.MakeMessage();
            if (prompt.GenerateMessages(preamble, promptMessage))
            {
                await context.PostAsync(preamble);
            }
            await context.PostAsync(promptMessage);
        }
        return prompt;
    });

    return form.Build();

}

这里的主要变化是在最后一次调用 FormBuilder 之后插入 form.Prompter 并且不立即返回新表单。被覆盖的提示使用一种“字符串”类型来调用您的自定义验证,并且您应该能够对不成功的条目以及其他任何内容执行您想要的操作。

于 2018-05-22T23:43:08.700 回答