Search code examples
node.jsbotframeworkdirect-line-botframeworkweb-chat

How to detect end of dialog in Webchat/DirectLine?


I created a simple chatbot with botbuilder in Node.js. Due to the given environment I included the chatbot via a custom iframe. The frontend is WebChat with DirectLine. How can I detect the end of the dialog in the parent window?

I could not find the correct way on how to catch the end of the dialog in the WebChat/DirectLine.

I render my chat with the following code inside the iframe:


const store = window.WebChat.createStore({}, ({ dispatch }) => next => action => {
 if (action.type === 'DIRECT_LINE/CONNECT_FULFILLED') {
  dispatch({
   type: 'WEB_CHAT/SEND_EVENT',
   payload: {
    name: 'webchat/join',
    value: { language: window.navigator.language }
   }
 });
}
 return next(action);
});

 window.WebChat.renderWebChat({
   directLine: window.WebChat.createDirectLine({ token: "thisismytoken" }),
   store,
   userID: '1',
   username: username,
   styleOptions: {
    botAvatarImage: "https://mylink.azurewebsites.net/avatar_user_1.png",
    userAvatarImage: "https://mylink.azurewebsites.net/avatar_user_1.png"
   }
 }, document.getElementById('webchat'));

In Node.JS I end the conversation with the following code:

            return await step.endDialog();

As soon as endDialog is run, I want to submit the parent of the iFrame. Can anyone give me some guidance?


Solution

  • To accomplish this requires only a few modifications to your code.

    First, in your bot, send an activity just prior to the step.endDialog() call. This activity will be an event and will send data with a status to be picked up by your page.

    In this example, I am including the user data so I can see who exited. I am also using the sendActivities() method so I can thank the user while simultaneously sending the event. You can, of course, just use sendActivity() to send the single event.

      async finalStep(step) {
        const user = stepContext.context.activity.from;
        const data = { user: user, dialogStatus: 'complete' };
    
        await stepContext.context.sendActivities([
          { text: 'Thank you for visiting my bot!', type: 'message' },
          { name: 'data', type: 'event', channelData: { 'data': data } }
        ]);
        return await stepContext.endDialog();
      }
    

    Next, in your page, use the createStore() method and check for the action.type of DIRECT_LINE/INCOMING_ACTIVITY. On any incoming activity, you will create a new Event called 'incomingActivity' which will take the received payload from the bot.

    You will also add a window.addEventListener of the same name, 'incomingActivity', which will capture the incoming data and parse it, as needed.

    Lastly, as you have already done in your code, pass the store into the renderWebChat component.

    const store = window.WebChat.createStore( {}, ( { dispatch } ) => next => action => {
      if ( action.type === 'DIRECT_LINE/INCOMING_ACTIVITY' ) {
        const event = new Event('incomingActivity');
    
        event.data = action.payload.activity;
        window.dispatchEvent(event);
      }
    
      return next( action );
    } );
    
    window.addEventListener('incomingActivity', ({ data }) => {
      const type = data.type;
      if (type === 'event' && data.channelData.data.dialogStatus) {
        const status = data.channelData.data.dialogStatus;
        const user = data.channelData.data.user;
        console.log(`User '${user.name}', with an id of '${user.id}', reached the end of the dialog`);
      }
    })
    

    Web Chat window:

    enter image description here

    Bot's console transcript logging:

    enter image description here

    Browser's developer console:

    enter image description here

    I use something like this fairly regularly so it should work for you.

    Hope of help!