多次调用FormDialog

时间:2016-06-23 08:29:56

标签: c# botframework botbuilder

我正在尝试创建一个向用户提问的机器人。在发布时,机器人会询问用户将选择哪个选项:

  1. 开始测验(他必须选择主题和测验的难度);
  2. 查看他在上一次测验中得到的分数;
  3. 重置分数。
  4. 测验的内容位于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内部服务器错误。

    这就是为什么我试着回忆一个“测试”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变量为null。

    那么,你知道如何正确地回忆我的FormBuilder在Bot框架模拟器上有10个问题吗?

    非常感谢!

2 个答案:

答案 0 :(得分:2)

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

ncombined <- merge(x = sample_sort, y = MBE[,c(1:4)], by = "id", all.x = TRUE)

它允许创建这样的脚本:

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();

答案 1 :(得分:0)

首先,谢谢你回答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。在本课程中,我提出了一些方法:

[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);
    }

AND,最后,将重新加载此过程的循环,直到显示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);
            }
        }
    }

希望它会有所帮助! :)