Is there a way to make bots aware of what page they are on?

122 views Asked by At

I have a chatbot that will eventually be deployed on multiple websites, and there are a number or variables that need to change based on the site (e.g. language, QnA Database, Dialog, etc.). I'd like to do this with a single bot, and just pass a variable so that it knows which page it is being rendered on (for a simple example, let's assume country pages: us, fr, de, etc.). I have been unsuccessful in passing this information to the bot.

Ideally this would be before the welcome message fires, but I can't even get it to send at all. I have a custom store set up:

            const store = window.WebChat.createStore({}, function(dispatch) { return function(next) { return function(action) {
                if (action.type === 'WEB_CHAT/SEND_MESSAGE') {
                    // Message sent by the user
                    PageTitleNotification.Off();
                    clearTimeout(interval);
                } else if (action.type === 'DIRECT_LINE/INCOMING_ACTIVITY' && action.payload.activity.name !== "inactive") {
                    // Message sent by the bot
                    clearInterval(interval);
                    interval = setTimeout(function() {
                        // Change title to flash the page
                        PageTitleNotification.On('Are you still there?');
                        
                        // Notify bot the user has been inactive
                        dispatch.dispatch({
                            type: 'WEB_CHAT/SEND_EVENT',
                            payload: {
                                name: 'inactive',
                                value: ''
                            }
                        });
                        
                    }, 300000)
                }

                return next(action);
            }}});

But for my use case I don't think what's in there actually matters, only that it is defined. The functions here just 1) clear an interval when the user sends a message and 2) set a new interval and send an inactivity message to the bot.

I also have a send message activity that is on a button click for a transcript. It looks like this:

            document.querySelector('#transcriptButton').addEventListener('click', function()  {
                return store.dispatch({
                    type: 'WEB_CHAT/SEND_MESSAGE',
                    payload: { text: 'Email me a transcript' }
                });
                /*return store.dispatch({
                    type: 'WEB_CHAT/SEND_EVENT',
                        payload: {
                            name: 'siteContext',
                            value: 'eatonchatbot indexBackup.html'
                    }
                });*/
            });

This sends a "front channel" message (that I can see in the bot) to request a transcript, which kicks off a dialog. That works. The commented out section alludes to what I'm trying to do. I have a separate dispatch statement as shown below, which has the exact same SEND_EVENT code as is commented out above. The SEND_EVENT does work as expected when it keys off the button click.

Here is the additional code I added. This is the piece that is NOT working. What I want is, when the bot has been rendered (but ideally before the welcome message), send this siteContext event to the bot so that I know where the bot is being rendered. I do not get any activity in the bot with this code. I also tried replacing it with SEND_MESSAGE instead of SEND_EVENT in a sort of reverse test from above, but that didn't work either.

            // Test setting site context
            store.dispatch({
                type: 'WEB_CHAT/SEND_EVENT',
                    payload: {
                    name: 'siteContext',
                    value: 'eatonchatbot indexBackup.html'
                }
            });

            /*store.dispatch({
                type: 'WEB_CHAT/SEND_MESSAGE',
                payload: {
                    text: 'eatonchatbot indexBackup.html'
                }
            });*/

It just occurred to me that this statement is probably running before the bot is rendered. So I put it in an interval and this DOES work. However, it does not fire the message until after the welcome message has been sent.

            setTimeout(function() {
                store.dispatch({
                    type: 'WEB_CHAT/SEND_EVENT',
                    payload: {
                        name: 'siteContext',
                        value: 'eatonchatbot indexBackup.html'
                    }
                });
                        
            }, 5000);

So this kind of works, but if this siteContext value was needed to determine the language of the welcome message, this would obviously fail. So my main ask here is, is there a better way to try to pass in a siteContext value like this, or is there some way to ensure that the context is received and can be used by the bot before the welcome message fires? I do see that there is a locale setting in the renderWebChat method, but I can't figure out if and how I could access that in the bot, and besides it may not be granular enough depending on the business need. But it seems if I could send some sort of value in that renderWebChat object, that might avoid all of the other crazy stuff I'm trying to do.

1

There are 1 answers

2
billoverton On BEST ANSWER

With some help from @Hessel and this issue I found on GitHub, I was able to come up with a solution. Just setting the values being passed in via onEvent (which I am now using in place of onTurn to reduce an if statement) isn't good enough if you need to alter content in the welcome message (e.g. language, user name, or an altogether different message). The onMembersAdded still fires before the values can be set, at least if you're setting them in userState. The key is to set up separate welcome messages in onEvent for directline and onMembersAdded for all other channels (I didn't include webchat as in the example as I'm not sending any event for that channel).

Here is the onEvent function I used:

        this.onEvent(async (context, next) => {

                // Check for inactivity
                if (context.activity.name && context.activity.name === 'inactive') {
                    await context.sendActivity({
                        text: 'Are you still there? Is there anything else I can help you with?',
                        name: 'inactive'
                    });
                }

                // Check for webchat/join event (directline conversation initiation)
                if (context.activity.name && context.activity.name === 'webchat/join') {
                    const userData = await this.userDialogStateAccessor.get(context, {});
                    userData.siteContext = context.activity.value;
                    
                    // Debug
                    console.log(`The current language is: ${userData.siteContext.language}`);
                    console.log(`The current page is: ${userData.siteContext.page}`);
                    //await context.sendActivity(`The current language is: ${userData.siteContext.language}`);
                    //await context.sendActivity(`The current page is: ${userData.siteContext.page}`);
                    
                    if (!userData.accountNumber) {
                        const dc = await this.dialogs.createContext(context);
                        await dc.beginDialog(AUTH_DIALOG);
                        await this.conversationState.saveChanges(context);
                        await this.userState.saveChanges(context);
                    } else {
                        if (context.activity.channelId == 'msteams') {
                            var welcomeCard = CardHelper.GetMenuCardTeams(welcomeMessage,'Y','Y');
                        } else {
                            var welcomeCard = CardHelper.GetMenuCard(welcomeMessage,'Y','Y');
                        }
                        await context.sendActivity(welcomeCard);
                        this.appInsightsClient.trackEvent({name:'conversationStart', properties:{accountNumber:userData.accountNumber}});
                    }

                    await this.userState.saveChanges(context);
                    
                }
            
            // By calling next() you ensure that the next BotHandler is run.
            await next();
        });

The event I used in the custom store is pretty much the same as above, except I updated it to pull in the most preferred language and current url (was hard coded above).

          store = window.WebChat.createStore({}, function (dispatch) {
            return function (next) {
              return function (action) {
                if (action.type === 'DIRECT_LINE/CONNECT_FULFILLED') {
                  dispatch.dispatch({
                    type: 'WEB_CHAT/SEND_EVENT',
                    payload: {
                      name: 'webchat/join',
                      value: {
                        language: navigator.languages[0],
                        page: window.location.href
                      }
                    }
                  });
                }

                return next(action);
              };
            };
          });

If you have your renderWebChat method inside a function that you can call so that your bot doesn't automatically start (I have a floating icon that causes the bot to load onclick) this should go outside that function.