2

I am developing a bot that is connected to our client application using Direct Line using botbuilder JS V4. for some reasons, messages are sent with a wrong order to the bot. For example:

  • User: hello

  • Bot: How can I help you?

  • Bot: Hi, I am a robot.

As a solution, I added a delay of two seconds between messages in many of my bot's dialogues.

What is the right way to do that? And is this the recommended way to fix this issue?

Using setTimeout()

async greetUser(step) {
  ...
  await step.context.sendActivity(firstReply);
  setTimeout(() => {
    await step.context.sendActivity(secondReply);
  }, 2000);
}

Using sleep()

const sleep = require("util").promisify(setTimeout);

async greetUser(step) {
  ...
  await step.context.sendActivity(firstReply);
  await sleep(2000);
  await step.context.sendActivity(secondReply);
}

After adding many of sleep() functions, I noticed that the bot performance is affected. The bot wasn't responding in production enivrement. and it thows this error:

[onTurnErrorStack]: Error
   at new RestError (D:\home\site\wwwroot\node_modules\@azure\ms-rest-js\dist\msRest.node.js:1397:28)
   at D:\home\site\wwwroot\node_modules\@azure\ms-rest-js\dist\msRest.node.js:1849:37
   at <anonymous>
   at process._tickDomainCallback (internal/process/next_tick.js:228:7)
[onTurnError]: {"statusCode":401,"request":{"streamResponseBody":false,"url":"https://directline.botframework.com/v3/conversations/DxjYZQsdv16sdULAsaIn8iQ-h/activities/DxjYZQ65dffgsaIn8iQ-h%7C0000000","method":"POST","headers":{"_

Any suggestions to resolve this issue?

Montacer Dkhilali
  • 390
  • 1
  • 6
  • 20

2 Answers2

4

If you are using the default BotFrameworkAdapter, you could also use the delay ActivityType. This will add a delay without extra code (source).

You could also give sendActivities method a try, according to the documentation this will also keep the order. The activities will be sent one after another in the order in which they're received. A response object will be returned for each sent activity. For message activities this will contain the ID of the delivered message.

async greetUser(step) {
    await step.context.sendActivities([
        { type: 'message', text: 'First reply' },
        { type: 'delay', value: 2000 },
        { type: 'message', text: 'Second reply' }
    ]);
}
Mick
  • 2,946
  • 14
  • 19
3

I had a similar issue where I was logging transcripts and the response would overwrite the request. I found a simple for loop that allowed me to set a ms delay and it worked great for me. The wait function is:

    async wait(milliseconds) {
        var start = new Date().getTime();
        for (var i = 0; i < 1e7; i++) {
            if ((new Date().getTime() - start) > milliseconds) {
                break;
            }
        }
    }

So you should be able to call it like this:

async greetUser(step) {
  ...
  await step.context.sendActivity(firstReply);
  await this.wait(2000);
  await step.context.sendActivity(secondReply);
}

Now this seems very similar to sleep function, which I do not have any experience with. I haven't had any issues with it and it is getting called twice per turn to log both the request and the response, though my delay is only 250ms, not 2000.

I would be interested to know if there is a better way to introduce conversation delay.

Edit: As mentioned in comments below, awaiting a Promise would be a better solution. But leaving the original answer here in case it is beneficial to someone. Inside async function, it seems that await new Promise(resolve => setTimeout(resolve,2000)); would provide same results without the drawbacks.

billoverton
  • 2,705
  • 2
  • 9
  • 32
  • 3
    This `wait` function introduces a busy loop which is not a good strategy in Node.js since it blocks the event loop and consumes CPU. More details: https://nodejs.org/uk/docs/guides/dont-block-the-event-loop/ – afenster Oct 04 '19 at 21:49
  • Noted. I ran across a solution to a similar issue which was solved by using `await new Promise(resolve => setTimeout(resolve,2000));`. Would that have similar issues to the wait function? – billoverton Oct 14 '19 at 15:15
  • 1
    No, this is fine. `await` will just wait on the promise without blocking the event loop. – afenster Oct 14 '19 at 22:48
  • This should work anywhere and have the same effect as the wait function defined above, no? I may try to work this in to other functions. – billoverton Oct 15 '19 at 17:42
  • 1
    Anywhere, but only inside `async` functions. And yes, it's *much* better than busy-waiting. – afenster Oct 16 '19 at 18:04
  • @afenster I finally tried to add this in my customLogger transcript middleware but it didn't work. Guess it's something about how the middleware processes the activities and logs to Cosmos, but when I replaced my wait function with awaiting the Promise it no longer captured both sides of the conversation (only the bot response). Your solution is still best for the OP's question but I wanted to add this comment here to let others know that in some cases a wait function may still be required. – billoverton Nov 05 '19 at 15:26