Microsoft BotFramework v4 Расписание задач и состояния - PullRequest
0 голосов
/ 13 января 2019

Я использую botframework v4 от Microsoft для создания бота. Я выполнил задачу с задержкой, чтобы проверить, не ответил ли пользователь за последние 2 часа. По истечении времени ожидания 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 голосов
/ 13 января 2019

Позвольте мне начать с указания, что запуск долгоживущих Task внутри бота не будет очень масштабируемым решением. Как и веб-приложения, боты, как правило, масштабируются на нескольких серверах, а также должны быть терпимы к процессу или перезапуску сервера. Вы, вероятно, захотите использовать какую-то внешнюю распределенную систему таймера, которая гарантирует, что, независимо от времени жизни вашего бота, таймер будет сохраняться и в конечном итоге вызываться. Кроме того, это не очень хорошее использование машинных ресурсов. Если у вашего бота 100 или, надеюсь, 1000 пользователей, и вы постоянно создаете Task с Task::Delay, то вы будете испытывать немало накладных расходов в плане ресурсов. Обычно решение, подобное этому, состоит в том, чтобы иметь хранилище таймеров, которое обслуживается одним работником.

Хорошо, оставим в стороне это предупреждение, давайте просто поговорим о конкретных проблемах, с которыми вы столкнулись:

  1. Я не могу отменить эту задачу, если пользователь уже сбросил диалог вручную через диалоги.

Ну, вы могли бы ... вам просто нужно создать компаньона CancellationTokenSource, передать его Token Task.Delay и ContinueWith, а затем, если вы хотите отменить его, вызвать его Cancel метод, который освобождает таймер задержки и гарантирует, что он никогда не вызывается.

Я не знаю, что именно EndConversation находится в вашем примере кода, но вместо того, чтобы просто быть Task, теперь нужно будет иметь структуру данных с Task и CancellationToken. Здесь может работать простой кортеж, иначе создайте себе новый класс.

  1. Состояния в Задании Задержки не обновляются. Например, если пользователь добавляет заметку в список, состояние в задаче задержки в конце диалога равно 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.");
    });
}

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...