我正在使用Microsoft的botframework v4来构建机器人。我实施了一项任务,但要延迟检查用户是否在最近2个小时内没有回答。如果达到2小时超时,该功能将执行一些操作并重置对话状态。效果很好,但是有两个问题:
我的延迟任务代码:
EndConversation = Task.Delay(600000).ContinueWith(async (t) =>
{
bool wordGenerated = false;
xyzState = await GetXYZState(dialogContext);
if (xyzState.ListCount > 0)
{
//retry 4 times sending the word document
for (int i = 0; i < 4; i++)
{
if (await GenerateWordDocument.CreateDoc(dc, _xyzAccessor, _xyzAccessor2))
{
wordGenerated = true;
break;
}
}
}...
答案 0 :(得分:2)
让我首先指出,在机器人内部启动寿命长的Task
并不是一个非常可扩展的解决方案。像Web应用程序一样,僵尸程序倾向于在多个服务器之间扩展,并且还必须容忍进程或服务器重启。您可能希望使用某种外部的分布式计时器系统,以确保无论机器人的生存时间如何,计时器都将保留并最终被调用。除此之外,这也不是对机器资源的充分利用。如果您的漫游器有100个用户,或者希望有1000个用户,并且您不断地用Task
创建Task::Delay
,则在资源方面将产生相当大的开销。通常,这样的解决方案将是由单个工作人员提供服务来存储计时器。
好吧,除了警告,我们只讨论您面临的具体问题:
- 如果用户已经通过对话框手动重置了对话,则我无法取消此任务。
好吧,您可以...您只需创建一个伴侣CancellationTokenSource
,将其Token
传递到Task.Delay
和ContinueWith
,然后,如果要取消它,请调用其Cancel
方法,该方法将释放延迟计时器并确保它从未被调用。
我不知道您的示例代码中到底是EndConversation
是什么,但是现在不仅仅是一个Task
,它现在需要是一个具有Task
和CancellationToken
。一个简单的元组可以在这里工作,否则可以为自己创建一个新的类。
- “延迟任务”中的状态未更新。例如,如果用户将注释添加到列表中,则在对话结束时,延迟任务中的状态为0。
是的,因此您看到的是过时状态,因为您将继续使用原始dialogContext
变量进行关闭。从技术上讲,您不应该在当前回合之外使用DialogContext
或ITurnContext
之类的东西。
您要在此处执行的操作称为主动消息传递。即使您的逻辑实际上并没有向用户发送消息,也适用相同的概念。因此,您要做的实际上是捕获闭包外部的ConversationReference
以进行继续,然后在闭包内部使用该ConversationReference
以稍后继续对话。看起来有点像这样:
// Capture the conversation reference from the current turn's activity
var currentConversationReference = dialogContext.Context.Activity.GetConversationReference();
// Capture the adapter of the current turn (this is fugly, but kind of the best way to do it right now)
var botAdapter = dialogContext.Context.Adapter;
// Kick off your timer with the continuation closing over these variables for later use
Task.Delay(600000).ContinueWith(async (t) =>
{
await botAdapter.ContinueConversationAsync(
YourBotsApplicationId,
currentConversationReference,
(proactiveTurnContext, ct) => {
// Now you can load state from the proactive turn context which will be linked to the conversation reference provided
var xyzState = await GetXYZState(proactiveTurnContext);
// Example of sending a message through the proactive turn context
await proactiveTurnContext.SendActivityAsync("Hi, I just did a thing because I didn't hear from you for two hours.");
});
}