4

If user doesn't respond to bot for more than 5 sec,Bot should prompt "are you there".To implement this logic, could you please help, how to set timer in chat bot using Node Js SDK and V4 version in Microsoft Bot framework

Srilekha
  • 57
  • 1
  • 2

1 Answers1

5

After a certain period of inactivity from the user, you can have WebChat or BotChat send a back-channel event to the bot, and the bot can respond by asking the user if they are still there. Note, I would recommend using WebChat since BotChat has been depreciated and the implementation in WebChat is slightly cleaner.

enter image description here

WebChat

In WebChat, we are going to create a custom store and middleware to listen for events where the user sends a message and when the bot sends a message - WEB_CHAT/SEND_MESSAGE and DIRECT_LINE/INCOMING_ACTIVITY respectively.

When the bot sends a message that isn't asking the user if they are still there, we will create a timeout that executes a callback after a set time frame - in this case, five seconds. The callback will dispatch a back-channel event to notify the bot that the user has been inactivity for more than the allotted time interval and the bot can respond accordingly. We will add a name to the back channel event - 'inactive' - so we can identify it on the bot side.

When the user sends a message, we will clear the timeout that was created when the bot sent a message, so the callback won't be executed since the user responded within the allotted time frame. See the code snippet below for more details.

let interval;

// We are using a customized store to add hooks to connect event
const store = window.WebChat.createStore({}, ({ dispatch }) => next => action => {

  if (action.type === 'WEB_CHAT/SEND_MESSAGE') {
    // Message sent by the user
    clearTimeout(interval);
  } else if (action.type === 'DIRECT_LINE/INCOMING_ACTIVITY' && action.payload.activity.name !== "inactive") {
    // Message sent by the bot
    clearInterval(interval);
    interval = setTimeout(() => {
      // Notify bot the user has been inactive
      dispatch({
        type: 'WEB_CHAT/SEND_EVENT',
        payload: {
          name: 'inactive',
          value: ''
        }
      });
    }, 5000)
  }

  return next(action);
  });

window.WebChat.renderWebChat({
directLine: window.WebChat.createDirectLine({ token }),
store,
}, document.getElementById('webchat'));

BotChat

We can create the same effect in BotChat - creating a timeout when the bot sends a message and clearing the timeout when the user sends a message; however, we have to create a custom DirectLine Object to see when the user sends a message and subscribe to and filter the activities so we can identify when the bot sent a message. See the code snippet below for more details.

let timeout;
let dl = new BotChat.DirectLine({ secret: <Secret> });

BotChat.App({
  botConnection: { ...dl,
    postActivity: activity => {
      // Listen for when the user sends a message and clear the timeout;
      clearTimeout(timeout);
      return dl.postActivity(activity);
    }
  },
  user: { id: 'userid' },
  bot: { id: 'botid' },
  resize: 'detect'
}, document.getElementById("bot"));

// Listen for incoming activities from the bot and create timeout
dl.activity$
  .filter(activity => activity.name !== 'inactive')
  .filter(activity => activity.from.id !== 'userid')
  .subscribe(activity => {

    clearTimeout(timeout);
    timeout = setTimeout(() => {
      // Notify bot the user has been inactive
      dl.postActivity({
        type: 'Event',
        value: '',
        from: {
          id: 'userid'
        },
        name: 'inactive'
      })
      .subscribe()
    }, 5000);
  })

Bot Code - Node

In the Bot's onTurn method, we are going to check if any incoming activities are named 'inactive.' If the activity is named inactive, send an activity asking if the user is still there; otherwise, continue with the normal bot dialog. We are also going to name the activity asking the user if they're there 'inactive' so we don't create a new timeout every five seconds while the user doesn't respond. See the code snippet below.

async onTurn(turnContext) {
    if(turnContext.activity.type === ActivityTypes.Event) {
        if (turnContext.activity.name && turnContext.activity.name === 'inactive') {
            await turnContext.sendActivity({
                text: 'Are you still there?',
                name: 'inactive'
            });
        } 
        ...
    }
    ...
}

Hope this helps!

tdurnford
  • 3,632
  • 2
  • 6
  • 15
  • Hi..Thanks a lot for the Quick Help. I tried implementing the above logic, but when a webpage is loaded, then in turnContext JSON there is no name with 'inactive'. _activity":{"type":"conversationUpdate","id":"5U8tzdNdNWA","timestamp":"2019-03-04T08:08:54.8394652Z","serviceUrl":"https://directline.botframework.com/","channelId":"webchat","from":{"id”:”YYY”},”conversation":{"id”:”AAA”},”recipient":{"id”:”XXX”,”name":"AD Swarm_Bot"},"membersAdded":[{"id”:”TTT”,”name":"AD Swarm_Bot"}]}} – Srilekha Mar 04 '19 at 08:47
  • The name attribute is added to the activity on the bot side and on the WebChat/BotChat side to identify when we need to set and clear the timeout. Take a look at the code snippets above. Are you using WebChat or BotChat? – tdurnford Mar 04 '19 at 17:05
  • Am using Webchat – Srilekha Mar 05 '19 at 05:18
  • Only some of the activities with type Message should have the name attribute. It looks like your bot is checking a Conversation Update for the name attribute which should throw an error since the activity won't have it. Check to make sure the activity is a message first. I've updated the bot code above to reflect this change. – tdurnford Mar 05 '19 at 16:54
  • @tdurnford I think this may no longer be accurate. It wasn't working for me and when monitored the activities this was showing up as type `Event`, not `Message`. Can you verify and update the answer if necessary? When I changed the condition to `turnContext.activity.type === ActivityTypes.Event` it worked fine. – billoverton Feb 06 '20 at 19:14
  • @tdurnford on a similar note, my bot had an `onMessage` function but not `onTurn`. I've currently got both running but I'm concerned that this may negatively impact performance. Should this all be consolidated into a single `onTurn` function with an appropriate condition to run the message steps only on `turnContext.activity.type === ActivityTypes.Message`? – billoverton Feb 06 '20 at 19:16
  • You are correct. It should be on event. I've updated my answer. `onTurn` is an older convention. You should use `onEvent` and `onMessage` handlers for your bot. – tdurnford Feb 06 '20 at 23:58