0

The old out of the box version of webchat used to remember a user across sessions when accessed through the same browser. I assume this was through the use of cookies? The new version ("gemini") does not do this. Every time I close the browser window and open again from the same browser, a new ID is sent, thus I no longer have access to saved userState information. To reclaim formatting, I switched over to botframework-webchat via directline and have the same issue. There is an explicit input for userID, and I have no idea how to make this consistent for a particular user.

I am not a web developer and have no need at this time to make this a robust implementation. I simply want to make this work the same way the former out of the box webchat worked. Can anyone shed some light on how that previous version worked, and/or how I can make the directline version have the same functionality?

Edit: Based on Steven's comment to the answer below, I tried to implement get/set cookies, but it is not working. Below is the code I tried. I can see through console that I am getting in to the if statement and userID is being generated, but it's not getting saved (I don't see it in Application>Storage>Cookies so it's not just the get not working). Please advise!

           function setCookie(name,value,days) {
                var expires = "";
                if (days) {
                    var date = new Date();
                    date.setTime(date.getTime() + (days*24*60*60*1000));
                    expires = "; expires=" + date.toUTCString();
                }
                document.cookie = name + "=" + (value || "")  + expires + "; path=/";
            }
            function getCookie(name) {
                var nameEQ = name + "=";
                var ca = document.cookie.split(';');
                for(var i=0;i < ca.length;i++) {
                    var c = ca[i];
                    while (c.charAt(0)==' ') c = c.substring(1,c.length);
                    if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
                }
                return null;
            }
            function eraseCookie(name) {   
                document.cookie = name+'=; Max-Age=-99999999;';  
            }

            if (!getCookie('userID')) {
                console.log('in if');
                var userID = Date.now() + '_' + Math.random().toString().substr(2, 9);
                console.log(userID);
                setCookie('userID',userID,999);
            }
            console.log('out of if');
billoverton
  • 2,705
  • 2
  • 9
  • 32

2 Answers2

0

Please look over the first half of this solution I posted. It goes over how to make a browser session persist when the user refreshes or temporarily navigates away.

In short, it relies on using sessionStorage() for storing the token and conversationId.

While it can be immensely useful to not have a session start over continuously, it can also be annoying during testing to have to close to and reopen or hard refresh and delete session storage to get a clean slate.

If it doesn't interfere with your page or your needs, place a button on your page and link it to the following bit of code. It's a simple hack that has been quite useful.

const reloadBtn = document.getElementById('reloadBtn');
reloadBtn.onclick = () => {
  location.reload( true );
};

window.onbeforeunload = function() {
  window.sessionStorage.removeItem( 'conversationId' );
  window.sessionStorage.removeItem( 'token' );
};

Hope of help!

Steven Kanberg
  • 6,078
  • 2
  • 16
  • 35
  • I'll check this our and see if I can adapt to my needs, but to be clear I'm trying to persist the userId, not conversationId. So that if I load up my browser the next day after shutting down my computer, my userState will still be preserved (in my bot's case, things like account number, last order searched for, other things I want to persist indefinitely). – billoverton Feb 26 '20 at 19:51
  • I don't know quite enough to try to adapt that solution to my needs. Can you provide guidance on how I could do this to store a userId (across multiple sessions, including shutdown of PC) instead of conversationId? My guy says maybe there is a simple cookie implementation to store the randomly generated userId locally and reuse that if it exists, but I don't have any idea how to approach that. – billoverton Feb 27 '20 at 14:35
  • I guess it depends on your needs. You can save userId via cookies by assigning it in Web Chat (`document.cookie = "userId=" + userId`...proper solution [here](https://stackoverflow.com/a/24103596/3962636)). If you are wanting to preserve aspects of a user's conversation for access later on, then you will need to use a state service (i.e. CosmosDB, Mongo, SQL, Blob storage). – Steven Kanberg Mar 03 '20 at 19:26
  • If, however, you are wanting to pickup the conversation where it left off, then keep in mind tokens expire after an hour and conversationId's / conversations are valid only for up to 24 hours. You can save and retrieve a conversation from your state service but it will only be for viewing the logged conversation. It won't pickup from that point. Using a state service would be within your bot. I don't know what language you are working in, but some community packages exist [here](https://github.com/BotBuilderCommunity/botbuilder-community-js). There are others in the wild, as well. – Steven Kanberg Mar 03 '20 at 19:33
  • First comment should address my issue. I'll give it a go and report back. I'm just concerned about remembering the user so I can access previous information in userState, not picking up the conversation or conversationState values. – billoverton Mar 03 '20 at 21:27
  • I'm still a bit lost here. How can I set the cookie if I don't know what it is (i.e. the first time the user interacts with the bot). Or was the old webchat creating an id before invoking the webchat? If I leave userID out when I call renderWebChat it will generate one for me but then I don't know how to find and save it. – billoverton Mar 11 '20 at 20:06
  • So I tried the code on the linked solution you provided, but it's not working. I can't get or set cookies. I believe that solution is 7 years old so it may no longer function. I haven't had any luck trying to Google something on my own. Any further guidance would be appreciated! – billoverton Mar 11 '20 at 20:42
  • Updated the question with my attempt at setting the cookie – billoverton Mar 11 '20 at 20:44
  • I was never able to get this working. Can you take a look at my cookie implementation (added to question) and advise what might be wrong? – billoverton Mar 30 '20 at 21:48
0

Ultimately I found that the way to handle this was by initiating the chat session via token. When you provide a user property in the body of the token request, it will properly initiate the session and pull up the correct user state (if user has conversed with the bot before). The documentation on initiating webchat via token is a little light. Here's how I did it.

First, you need to create an endpoint in your bot to generate the token (I'm passing in userId from browser, which you'll see in a minute).

server.post('/directline/token', async (req, res) => {

    try {
        var body = {User:{Id:req.body.userId}};
        const response = await request({
            url: 'https://directline.botframework.com/v3/directline/tokens/generate',
            method: 'POST',
            headers: { Authorization: `Bearer ${process.env.DIRECTLINE_SECRET}`},
            json: body,
            rejectUnauthorized: false
        });
        const token = response.token;
        res.setHeader('Content-Type', 'text/plain');
        res.writeHead(200);
        res.write(token);
        res.end();
    } catch(err) {
        console.log(err);
        res.setHeader('Content-Type', 'text/plain');
        res.writeHead(500);
        res.write('Call to retrieve token from Direct Line failed');
        res.end();
    }
})

You could return JSON here, but I chose to return token only as text. Now to call the function, you'll need to hit this endpoint from the script wherever you are deploying the bot (this is assuming you are using botframework-webchat CDN). Here is the code I used for that.

    const response = await fetch('https://YOURAPPSERVICE.azurewebsites.net/directline/token', {
        method: 'POST',
        headers: {'Content-Type':'application/json'},
        body: JSON.stringify({userId:userID})
    });
    const token = await response.text();

Body of request must be stringified JSON. Fetch returns the response as a stream, so you need to convert it using .text() or .json() depending on how you are sending the response from your bot endpoint (I used .text()). You need to await both the fetch AND the response.text(). My whole script to deploy the webchat is within an async function.

Combined with the cookie implementation mentioned above for setting and recalling the userID, this allowed me to remember the user as long as they still have the cookie in their browser. Note that this will work only for Directline. If you are using the OOTB webchat it no longer remembers you. I would say in its current implementation the OOTB webchat is suited only for testing and I wouldn't recommend using it in any production capacity.

billoverton
  • 2,705
  • 2
  • 9
  • 32