Search code examples
botframework

Webchat Value Exchanging Issue on Botframework V4


I'm using Direct Line, MS Speech Service and the Webchat.js on bot running on Botframework V4.
I start the bot from a web page using webchat.js passing values to the back end app like that https://github.com/microsoft/BotFramework-WebChat/tree/master/samples/04.api/d.post-activity-event
Code used to instanciate the bot on web channel:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width">
    <title>FTD Bit_Bot</title>
</head>
<body style="background-color: #fff; background-image: url('img/FTDBackground.jpg');">
    <div id="chatbot" role="main"
         data-cb-width="320px"
         data-cb-height="400px"
         data-cb-border="1px solid #ededed"
         data-cb-border-radius="0px"
         data-cb-background-color="white"
         data-cb-position="fixed"
         data-cb-right="40px"
         data-cb-bottom="100px"
         data-cb-zIndex="1"
         data-cb-bubble-background="rgba(217, 217, 217, 0.15)"
         data-cb-bubble-from-user-background="#e22e2c"
         data-cb-bubble-from-user-text-color="white"
         data-cb-bubble-max-width="600"
         data-cb-bot-avatar-image="http://demo.radical-thinking.net/bella/microsoft/chat-assets/img/Chat-in-day-Workshop-Icon.png"
         data-cb-hide-send-box="false"
         data-cb-hide-upload-button="true"
         data-cb-send-box-button-color="#e22e2c"
         data-cb-send-box-button-color-on-disabled="#CCC"
         data-cb-send-box-button-color-on-focus="#333"
         data-cb-send-box-button-color-on-hover="#333"
         data-cb-send-box-height="30"
         data-cb-suggested-action-text-color="black"
         data-cb-suggested-action-border="solid 2px #e22e2c"
         data-cb-suggested-action-height="30">
    </div>
    <!-- Include ajax library for speech service token request call -->
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js"></script>
    <!-- Include webchat client functionality (https://github.com/Microsoft/BotFramework-WebChat) -->
    <!-- Change to the latest directory to stop the 'No renderer for this activity' error. However the avatar won't show -->
    <script src="https://cdn.botframework.com/botframework-webchat/latest/webchat.js" charset="utf-8"></script>
    <script>
        var chatbot = document.getElementById('chatbot');

        // Style modifications
        const styleOptions = {

            // Colors
            bubbleBackground: chatbot.dataset.cbBubbleBackground,
            bubbleFromUserBackground: chatbot.dataset.cbBubbleFromUserBackground,
            bubbleFromUserTextColor: chatbot.dataset.cbBubbleFromUserTextColor,
            bubbleMaxWidth: parseInt(chatbot.dataset.cbBubbleMaxWidth), // maximum width of text message

            // Avatar
            botAvatarImage: chatbot.dataset.cbBotAvatarImage,
            userAvatarInitials: '',

            // Send box
            hideSendBox: JSON.parse(chatbot.dataset.cbHideSendBox),
            hideUploadButton: JSON.parse(chatbot.dataset.cbHideUploadButton),
            sendBoxButtonColor: chatbot.dataset.cbSendBoxButtonColor,
            sendBoxButtonColorOnDisabled: chatbot.dataset.cbSendBoxButtonColorOnDisabled,
            sendBoxButtonColorOnFocus: chatbot.dataset.cbSendBoxButtonColorOnFocus,
            sendBoxButtonColorOnHover: chatbot.dataset.cbSendBoxButtonColorOnHover,
            sendBoxHeight: parseInt(chatbot.dataset.cbSendBoxHeight),

            // Suggested actions
            suggestedActionTextColor: chatbot.dataset.cbSuggestedActionTextColor,
            suggestedActionBorder: chatbot.dataset.cbSuggestedActionBorder,
            suggestedActionHeight: parseInt(chatbot.dataset.cbSuggestedActionHeight),

        }
    </script>
    <script>
        var chatbot = document.getElementById("chatbot");

        //style tag start
        var script = {
            id: 'chatbotStyle',
            type: 'text/css',
            style: document.createElement('style'),
            content: '@import url("https://fonts.googleapis.com/css?family=Roboto:300,400,600"); ::-webkit-scrollbar{width:10px}::-webkit-scrollbar-track{background:#f1f1f1}::-webkit-scrollbar-thumb{background:#888}::-webkit-scrollbar-thumb:hover{background:#555} #chatbot{ font-family: Roboto, sans-serif; font-weight: 400; position: fixed; right: 10px; bottom: 20px; text-align: center; } #chatbot img{ margin: auto; } #chatbot small{ display: block; font-size: 9px; } .initLaunchPad{ cursor: pointer; text-align: center; transition: .4s all; } .botHeader{min-height:40px;background:#ededed;border-radius:6px 6px 0 0;display:flex;justify-content:space-between;align-items:center;padding:4px} .botHeader img{float:left;width:32px;height:32px; border-radius:50%;background-color:#f9f9f9}.botHeader h5{float:left;padding:0!important;margin:4px 0 3px 6px} .botHeader h5 small{font-size: 12px;} .botHeader > div{width:40%;display:flex;align-items:center;text-align:left;height:32px} #botClose{cursor:pointer;font-size:12px;font-weight:600;padding:6px 8px;border-radius:4px;background-color:#e22e2c;color:#fff; } #botContainer{ display: none; border-radius: 8px; border:1px solid #cbcbcb; transition:.4s all; width: 310px; height: 420px; max-width:420px; max-height: 420px; margin-bottom: 4px; } #botBody{ height: 366px; } ',
            append: function () {
                this.style.type = this.type;
                this.style.appendChild(document.createTextNode(this.content));
                document.head.appendChild(this.style);
            }
        }; script.append();
        //style tag end

        //initLaunchPad started
        var initLaunchPad = document.createElement('div');
        initLaunchPad.classList.add('initLaunchPad');
        var initBotIcon = document.createElement('img');
        initBotIcon.src = "https://farm5.staticflickr.com/4876/39891228293_13c532f352_o.gif";
        initBotIcon.width = 86;
        initLaunchPad.appendChild(initBotIcon);
        chatbot.appendChild(initLaunchPad);

        $(function getSpeechAuthToken() {
            var params = {
                // Request parameters
            };

            $.ajax({
                url: "https://brazilsouth.api.cognitive.microsoft.com/sts/v1.0/issuetoken" + $.param(params),
                beforeSend: function (xhrObj) {
                    // Request headers
                    xhrObj.setRequestHeader("Ocp-Apim-Subscription-Key", "XXXXXX");
                },
                type: "POST",
                // Request body
                data: "{body}",
            })
                .done(function (data) {
                    speechAuthToken = data;
                })
                .fail(function () {
                    alert("error");
                });
        });
        initBotIcon.addEventListener('click', function (e) {
            e.preventDefault();
            initLaunchPad.style.display = "none";
            botContainer.style.display = "block";

            /*Call bot API*/
            (async function () {

                const searchParams = new URLSearchParams(window.location.search);

                //Speech Service Token Generation
                const subscriptionKey = 'XXXXXX';
                const region = 'brazilsouth';
                let webSpeechPonyfillFactory;

                //Speech Service Setting
                webSpeechPonyfillFactory = await window.WebChat.createCognitiveServicesSpeechServicesPonyfillFactory({
                    credentials: {
                        authorizationToken: speechAuthToken,
                        region: region
                    }
                });

                //Direct-Line BotToken Generation
                const res = await fetch('https://directline.botframework.com/v3/directline/tokens/generate', { method: 'POST', headers: { Authorization: 'Bearer ' + 'XXXXXX' } });
                const { token } = await res.json();

                window.WebChat.renderWebChat({
                    directLine: window.WebChat.createDirectLine({ token }),
                    store,
                    userID: "User",
                    styleOptions,
                    locale: 'pt-BR',
                    selectVoice: (voices, activity) =>
                        activity.locale === 'pt-BR'
                            ? voices.find(({ name }) => /Daniel/iu.test(name))
                            : voices.find(({ name }) => /Daniel/iu.test(name)) ||
                            voices.find(({ name }) => /Daniel/iu.test(name)),
                    webSpeechPonyfillFactory
                }, document.getElementById('botBody'));
                document.querySelector('#botBody > *').focus();

                var mainTag = chatbot.getElementsByClassName('main')[0];
                mainTag.style.borderTop = chatbot.dataset.cbBorder;
            })().catch(err => console.error(err));

            //Bot Message Exchange Setting 
            const store = window.WebChat.createStore({}, ({ dispatch }) => next => action => {
                if (action.type === 'DIRECT_LINE/CONNECT_FULFILLED') {
                    dispatch({
                        type: 'WEB_CHAT/SEND_EVENT',
                        payload: {
                            name: 'userName',
                            value: Paul
                        }
                    });
                    dispatch({
                        type: 'WEB_CHAT/SEND_EVENT',
                        payload: {
                            name: 'userGenre',
                            value: male
                        }
                    });
                    dispatch({
                        type: 'WEB_CHAT/SEND_EVENT',
                        payload: {
                            name: 'activityName',
                            value: chatBotMission
                        }
                    });
                }
                return next(action);
            });

        //initLaunchPad ended

        var botContainer = document.createElement('div');
        botContainer.id = "botContainer";

        var botHeader = document.createElement('div');
        botHeader.classList.add('botHeader');

        var botClose = document.createElement('a');
        botClose.id = "botClose";
        botClose.innerText = "X";
        botClose.title = "Close";
        botHeader.innerHTML = "<div><img src='" + chatbot.dataset.cbBotAvatarImage + "'/> <h5>Bit_Bot <small>FTD Educação</small> </div></h5>";
        botHeader.appendChild(botClose);

        botClose.addEventListener('click', function (e) {
            e.preventDefault();
            initLaunchPad.style.display = "";
            botContainer.style.display = "none";
            return false;
        });

        botContainer.appendChild(botHeader);

        var botBody = document.createElement('div');
        botBody.id = "botBody";
        botContainer.appendChild(botBody);

        var poweredBy = document.createElement('div');
        poweredBy.innerHTML = "<small>Powered by <img src='http://ainetw.com/img/icon.png' width=12 /> AI Networks</small>";

        chatbot.appendChild(botContainer);
        chatbot.appendChild(poweredBy);
    </script>

</body>
</html>

Code snippet of the bot sent data collection in my bot:

 public async Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken = default(CancellationToken))
        {
            var studentProfile = await _studentProfileAccessor.GetAsync(turnContext, () => new StudentProfile(), cancellationToken);
            DialogContext dc = await _dialogs.CreateContextAsync(turnContext, cancellationToken);

            //Bot Channel Message Exchange Collection 
            if (turnContext.Activity.Name == "userName")
            {   
                studentProfile.Name = turnContext.Activity.Value.ToString();                   
                studentName = turnContext.Activity.Value.ToString();
            }
            if (turnContext.Activity.Name == "userGenre")
            {
                studentProfile.Gender = turnContext.Activity.Value.ToString();
                conversationSessionID = turnContext.Activity.From.Id;
                if (conversationSessionID.ToLower() == "user")
                {
                    var myConversationSessionID = turnContext.Activity.Id;
                    if (myConversationSessionID.IndexOf("|") != -1)
                    {
                        conversationSessionID = myConversationSessionID.Substring(0, myConversationSessionID.IndexOf("|"));
                    }
                }
                studentGender = turnContext.Activity.Value.ToString();
        //Variables persistance on DB
                SessionVariable SessionVariable = new SessionVariable();
                SessionVariable.AddSessionVariable(BellaMain.GlobalVariable.SystemID, BellaMain.GlobalVariable.CustomerID, conversationSessionID, "StudentName", studentName, "StudentGender", studentGender, "", "", "", "", "", "", "", "", "", "", "", 0);
            }
            if (turnContext.Activity.Name == "activityName")
            {
                if (turnContext.Activity.Value.ToString().ToLower() =="atividade2")
                {
                    var message = $"**Olá {studentName}, bem vindo ao Quant Bot!**";
                    await turnContext.SendActivityAsync(message);
                    await dc.BeginDialogAsync(Activity2MainDialog, "activity2MainDialog", cancellationToken);
                }
                else
                {
                    var message = $"**Olá {studentName}, bem vindo ao Quant Bot!**";
                    await turnContext.SendActivityAsync(message);                    
                }
            }

The application works fine but when there is more than one user connected to it exchanged data gets mismatched many times e.g.
Webclient 1 sends user name: Paul and Gender: male ==> app receives Paul and male
Webclient 2 sends user name: Mary and Gender: female ==> app sometimes receives Paul and male !? It should receive Mary and female instead
I have a feeling the sent data is buffered on Azure or somewhere else and needs to be cleared/refreshed every time a new web app bot instance starts.
Does it make sense? How can it be fixed? Thx


Solution

  • First, in case you are actually doing this, don't pass your Direct Line authorization in your client. This is a major security issue and you open yourself up to being breached.

    As for your issue, consider generating the unique userID and passing it in the createDirectLine() token call. In this way, the userID will be baked into the token and will not specifically be a function of the page. As the token should be unique to each user, this should help, if not solve, your user crossover issue.

    If it doesn't, I would recommend removing the Direct Line direct API call from your Web Chat code. In my case, I have my own token generating server which I call from Web Chat. The token server makes the call to Direct Line when my server's API is called by Web Chat. I also generate the userID in the token server so it is baked into the token which is then returned to Web Chat for use.

    If neither of these options fix your issue, then I would look closer at your external data collection, which you mentioned elsewhere.

    Hope of help!