回调 FormDialog 定义的次数
Calling back a FormDialog a defined number of times
我正在尝试创建一个可以向用户提问的机器人。在启动时,机器人会询问用户会做出哪个选择:
- 开始小测验(之后他必须选择小测验的主题和难度);
- 查看他上次测验的分数;
- 重置分数。
测验的内容在 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);
}
}
}
希望对您有所帮助! :)
我正在尝试创建一个可以向用户提问的机器人。在启动时,机器人会询问用户会做出哪个选择:
- 开始小测验(之后他必须选择小测验的主题和难度);
- 查看他上次测验的分数;
- 重置分数。
测验的内容在 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);
}
}
}
希望对您有所帮助! :)