Microsoft BotFramework v4任务计划和状态

时间:2019-01-13 15:46:00

标签: c# azure task botframework azure-bot-service

我正在使用Microsoft的botframework v4来构建机器人。我实施了一项任务,但要延迟检查用户是否在最近2个小时内没有回答。如果达到2小时超时,该功能将执行一些操作并重置对话状态。效果很好,但是有两个问题:

  1. 如果用户已经通过对话框手动重置了对话,则我无法取消此任务。
  2. “延迟任务”中的状态未更新。例如,如果用户将注释添加到列表中,则在对话结束时,延迟任务中的状态为0。

我的延迟任务代码:

 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;
                        }
                    }
                }...

1 个答案:

答案 0 :(得分:2)

让我首先指出,在机器人内部启动寿命长的Task并不是一个非常可扩展的解决方案。像Web应用程序一样,僵尸程序倾向于在多个服务器之间扩展,并且还必须容忍进程或服务器重启。您可能希望使用某种外部的分布式计时器系统,以确保无论机器人的生存时间如何,计时器都将保留并最终被调用。除此之外,这也不是对机器资源的充分利用。如果您的漫游器有100个用户,或者希望有1000个用户,并且您不断地用Task创建Task::Delay,则在资源方面将产生相当大的开销。通常,这样的解决方案将是由单个工作人员提供服务来存储计时器。

好吧,除了警告,我们只讨论您面临的具体问题:

  
      
  1. 如果用户已经通过对话框手动重置了对话,则我无法取消此任务。
  2.   

好吧,您可以...您只需创建一个伴侣CancellationTokenSource,将其Token传递到Task.DelayContinueWith,然后,如果要取消它,请调用其Cancel方法,该方法将释放延迟计时器并确保它从未被调用。

我不知道您的示例代码中到底是EndConversation是什么,但是现在不仅仅是一个Task,它现在需要是一个具有TaskCancellationToken。一个简单的元组可以在这里工作,否则可以为自己创建一个新的类。

  
      
  1. “延迟任务”中的状态未更新。例如,如果用户将注释添加到列表中,则在对话结束时,延迟任务中的状态为0。
  2.   

是的,因此您看到的是过时状态,因为您将继续使用原始dialogContext变量进行关闭。从技术上讲,您不应该在当前回合之外使用DialogContextITurnContext之类的东西。

您要在此处执行的操作称为主动消息传递。即使您的逻辑实际上并没有向用户发送消息,也适用相同的概念。因此,您要做的实际上是捕获闭包外部的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.");
    });
}