回调 FormDialog 定义的次数

Calling back a FormDialog a defined number of times

我正在尝试创建一个可以向用户提问的机器人。在启动时,机器人会询问用户会做出哪个选择:

  1. 开始小测验(之后他必须选择小测验的主题和难度);
  2. 查看他上次测验的分数;
  3. 重置分数。

测验的内容在 XML 文件中。我已经将相关的问题和答案存储在一个结构中。

FormBuilder 是:

[Serializable]
public class QuizQuestionsLoader
{
    public QuizQuestion Question { get; set; }

    public static IForm<QuizQuestionsLoader> QuestionLoaderForm(QuizQuestion question)
    {
        return new FormBuilder<QuizQuestionsLoader>()
            .Field(new FieldReflector<QuizQuestionsLoader>(nameof(Question))
                .SetType(null)
                .SetDefine(async (state, field) =>
                {
                    field
                        .AddDescription(state.Question.QuestionText, state.Question.QuestionText)
                        .AddTerms(state.Question.QuestionText, state.Question.QuestionText);

                    return true;
                }))
            .AddRemainingFields()
            .Build();
    }
}

因此,我制作了一个带有开关的 IDialog,用于确定用户做出的选择。如果用户选择开始测验,DefaultCase 将被激活:

new DefaultCase<QuizChoices?, IDialog<string>>((context, value) =>
                {
                    return Chain.From(() => FormDialog.FromForm(QuizStart.QuizForm, FormOptions.PromptInStart))
                        .Select(c => c.category)
                        .ContinueWith(async (ctx, res) =>
                        {
                            CategoryOptions? category = await res;
                            IList<QuizQuestion> questions = QuestionsLoader.LoadQuestions(category.Value.ToString().ToLowerInvariant()).ToList();

                            QuizQuestion currentQuestion = questions[0];
                            var questionsDialogs = Chain.From(() => FormDialog.FromForm(() => { return QuizQuestionsLoader.QuestionLoaderForm(currentQuestion); })).PostToUser();

                            for (int i = 1; i < questions.Count(); i++)
                            {
                                currentQuestion = questions[i];

                                questionsDialogs.ContinueWith(async (forctx, fores) =>
                                {
                                    await fores;
                                    return Chain.From(() => FormDialog.FromForm(() => { return QuizQuestionsLoader.QuestionLoaderForm(currentQuestion); }));
                                }).PostToUser();
                            }

                            return Chain.Return(questionsDialogs).Unwrap();
                    })
                    .ContinueWith(async (ctx, res) =>
                    {
                        await res;
                        return Chain.Return("Quiz fini !");
                    });
                })

我想向用户显示 10 个问题,这就是为什么我认为召回 FormBuilder 是个好主意,因为我不知道如何通过其他方式做到这一点。当我构建 运行 时,选择难度后,Bot Framework 模拟器会发送 500 内部服务器错误。

这就是为什么我试图回忆起一个"test" FormBuilder,它由一个简单的消息和三个选项组成,看看我是否可以用for循环回忆起FormDialog。 这是 FormBuilder :

public enum TestOptions
{
    A, B, C
}

[Serializable]
public class Test
{
    public TestOptions? choice;

    public static IForm<Test> TestForm()
    {
        return new FormBuilder<Test>()
            .Message("Test")
            .Field(nameof(choice))
            .Build();
    }
}

这是 IDialog :

return Chain.From(() => FormDialog.FromForm(Test.TestForm, FormOptions.PromptInStart))
                        .ContinueWith(async(ctx, res) =>
                        {
                            await res;
                            var testDialog = Chain.From(() => FormDialog.FromForm(() => { return Test.TestForm(); })).PostToUser();

                            for (int i = 0; i < 2; i++)
                            {
                                testDialog.ContinueWith<Test, Test>(async (forctx, fores) =>
                                {
                                    await fores;
                                    return Chain.From(() => FormDialog.FromForm(Test.TestForm, FormOptions.PromptInStart));
                                });
                            }

                            return Chain.Return(testDialog);
                        })

有了这个,FormDialog会显示一次,但是我看到执行了for循环。但是,testDialog 变量为空。

那么,您知道我如何继续正确回忆我的 FormBuilder 在 Bot Framework Emulator 上有 10 个问题吗?

非常感谢!

我添加了一个示例,说明如何使用此 commit 遍历测验中的问题。它使用名为 FoldDialog 的可链接对话框按顺序调用一系列对话框并聚合响应:

var quiz = Chain
            .PostToChain()
            .Select(_ => "how many questions?")
            .PostToUser()
            .WaitToBot()
            .Select(m => int.Parse(m.Text))
            .Select(count => Enumerable.Range(0, count).Select(index => Chain.Return($"question {index + 1}?").PostToUser().WaitToBot().Select(m => m.Text)))
            .Fold((l, r) => l + "," + r)
            .Select(answers => "your answers were: " + answers)
            .PostToUser();

它允许创建这样的脚本:

"hello",
"how many questions?",
"3",
"question 1?",
"A",
"question 2?",
"B",
"question 3?",
"C",
"your answers were: A,B,C"

首先感谢您的回答 Will Pornoy !

但是,在您回答之前我已经成功解决了我的问题,如下所示:

new DefaultCase<QuizChoices?, IDialog<string>>((context, value) =>
                {
                    return Chain.From(() => FormDialog.FromForm(QuizStart.QuizForm, FormOptions.PromptInStart))
                        .Select(c => new QuizParameters
                        {
                            CategoryParameter = c.category.Value.ToString(),
                            DifficultyParameter = c.difficulty.Value.ToString()
                        })
                        .ContinueWith<QuizParameters?, int>(async (ctx, res) =>
                        {
                            await res;

                            IList<QuizQuestion> questions = QuestionsLoader.LoadQuestions(QuizParameters.CategoryParameter, QuizParameters.DifficultyParameter).ToList();
                            return new QuizQuestionsLoader(questions);
                        })

其中 QuizParameter 是一个结构,其中包含用户的类别和难度选择。 我只得到一个 IList,其中包含问题文本及其答案。

最后,我将它传递给一个新对象QuizQuestionLoader。在这个class中,我做了一些方法:

[Serializable]
public class QuizQuestionsLoader : IDialog<int>
{ 
    public static int Score { get; private set; }

    private IList<QuizQuestion> problems;
    private QuizQuestion theQuestion;

    private int index;      
    private int jokerCount = 2;
    private const string jokerAnswerText = "Utiliser un joker";

    public QuizQuestionsLoader(IList<QuizQuestion> problems)
    {
        this.problems = problems;
    }

每次开始测验时调用的Task方法:

public async Task StartAsync(IDialogContext context)
    {
        problems.Shuffle();

        DisplayQuestion(context);
    }

一个DisplayQuestion方法重载(第一个是针对没有小丑离开的情况):

private void DisplayQuestion(IDialogContext context)
    {
        DisplayQuestion(context, false);
    }

    private void DisplayQuestion(IDialogContext context, bool useJoker)
    {
        theQuestion = problems[index];
        string questionText = theQuestion.QuestionText;
        IList<Answer> answers = theQuestion.Answers.ToList();

        if (useJoker)
        {
            IList<Answer> randomBadAnswers = answers.Where(a => !a.IsCorrect).ToList();
            randomBadAnswers.Shuffle();
            randomBadAnswers = randomBadAnswers.Take(2).ToList();

            answers = answers.Except(randomBadAnswers).ToList();
        }
        else if (jokerCount > 0)
        {
            Answer jokerAnswer = new Answer
            {
                AnswerText = $"{jokerAnswerText} ({jokerCount}) restant(s)"
            };

            answers.Add(jokerAnswer);  
        }

        PromptDialog.Choice(context, CheckResponseAsync, answers, questionText, null, 0, PromptStyle.Auto);
    }

最后,循环将重新加载此过程,直到显示 10 个问题:

public async Task CheckResponseAsync(IDialogContext context, IAwaitable<Answer> argument)
    {
        Answer answer = await argument;

        if (answer.AnswerText.StartsWith(jokerAnswerText))
        {
            jokerCount--;
            await context.PostAsync("Suppression de deux mauvaises réponses...");
            DisplayQuestion(context, true);
        }
        else
        { 
            await context.PostAsync(answer.IsCorrect ? "Bonne réponse !" : "Mauvaise réponse !");
            index++;

            Answer goodAnswer = theQuestion.Answers.First(a => a.IsCorrect);

            if (answer.AnswerText == goodAnswer.AnswerText)
            {
                Score++;
            }

            if (index < problems.Count)
            {
                DisplayQuestion(context);
            }
            else
            {
                await context.PostAsync($"Votre score est de {Score}");
                context.Done(Score);
            }
        }
    }

希望对您有所帮助! :)