How-to: Store user preferences for your skills

Objective

This walkthrough will show you how to save the preferences of users of your skills in a simple concise way. Storing the preferences of your users allows you to provide different interactions for returning users versus new users of your skill as well as customize your skill for those who have specific needs. In this particular walkthrough, we will simply store a boolean of whether the user is new or has run the skill before at least once, but this setup can be expanded to store and retrieve any other information you wish for that user.

Prerequisites

You’ll need a working version of the Jibo SDK installed on your computer.

Steps

1) Open any existing project in your Jibo SDK that you’d like to integrate user preferences into. Your project must have a Listen or ListenJS behavior with the “heyJibo” option set to true somewhere at the beginning of your skill. This allows us to retrieve the users identity which is an absolute requirement for storing their preferences. Alternatively, you can download this main.bt file as a starting point.
main.bt (6.1 KB)

2) Add the npm module ‘nedb’ to your project. To do this, just navigate your console to your projects folder and npm install nedb. The nedb module allows connections to the super simple Javascript-based NeDB database. If you’re comfortable with another Node-connected database, feel free to use that one, but we’ll use NeDB for this tutorial.

3) Get the users identity when they say “Hey Jibo”. To do this, you simply need to check to see if an identity was returned by the API via the listener. Edit the Listen (or ListenJS) behavior’s onResult argument with the following code:

(listener) => {
    listener.on('hey-jibo', (asrResult, speaker) => {
	// Check if we have an identity for this speaker
	if(speaker.speakerIdStatus=="ACCEPTED"){
		// Store current speaker name globally
		notepad.currentSpeaker = speaker.speakerIds[0];
	}
	emitter.emit('listen');
    });
}

As you can see above, we edited the default hey-jibo listener with a quick check to see if speaker.speakerIdStatus returns the string “ACCEPTED” meaning that we know the identity of who just spoke. Otherwise you’ll get the string "NO-SPEAKERS-TO-LIST". If we do get an identity, we immediately pull in the first speaker ID from the speakerIds array which holds their name and store it globally for our skill. That means you can use notepad.currentSpeaker from that point on in your skill to reference the current user by name (e.g. “Good morning, Steve”).

4) Connect to the database. We further update the Listen behavior by connecting to our database via a table with the users name, giving us a unique storage area for each user. Update the Listen (or ListenJS) behavior’s onResult argument with the following code:

(listener) => {
    listener.on('hey-jibo', (asrResult, speaker) => {
	// Check if we have an identity for this speaker
	if(speaker.speakerIdStatus=="ACCEPTED"){
		// Store current speaker name globally
		notepad.currentSpeaker = speaker.speakerIds[0];
		
		// Prepare our speaker name by removing non-alphanumeric characters
		notepad.currentSpeakerClean = speaker.speakerIds[0].replace(/\W/g,'');
		
		// Create (or connect to existing) database for this users prefs
		var NeDB = require('nedb');
		var nUserPrefsDB = new NeDB({
			filename:'db/'+notepad.currentSpeakerClean+'_prefs.json',
			autoload:true
		});
	}
	emitter.emit('listen');
    });
}

Our hey-jibo listener code now has a cool little database connection. Firstly, you’ll notice that we create a new notepad.currentSpeakerClean variable with a version of the user’s name which is free from non-alphanumeric characters (e.g. “James O’Reilly” becomes “JamesOReilly”) which lets us use that name for special things like our new database name.

The next 5 lines connect to NeDB and automatically loads a database file with the user’s name (e.g. JamesOReilly_prefs.json) in a folder called “db”. If that file/folder doesn’t exist, in the case of first time users, nedb NeDB is smart enough to create it for you on the fly.

5) Track a returning user. In our final (and biggest) update of the Listen behavior code, we add code to check if our user is new or returning, then update the database accordingly with a firstAccess boolean. Update the Listen (or ListenJS) behavior’s onResult argument with the following code:

(listener) => {
    listener.on('hey-jibo', (asrResult, speaker) => {
	// Check if we have an identity for this speaker
	if(speaker.speakerIdStatus=="ACCEPTED"){
		// Store current speaker name globally
		notepad.currentSpeaker = speaker.speakerIds[0];
		
		// Prepare our speaker name by removing non-alphanumeric characters
		notepad.currentSpeakerClean = speaker.speakerIds[0].replace(/\W/g,'');
		
		// Create (or connect to existing) database for this user's prefs
		var NeDB = require('nedb');
		var nUserPrefsDB = new NeDB({
			filename:'db/'+notepad.currentSpeakerClean+'_prefs.json',
			autoload:true
		});
		
		// Check to see if new or returning user and update database accordingly
		nUserPrefsDB.find({}, function(err,docs){
			if(!docs.length){
				// Save preferences for new users
				nUserPrefsDB.insert({
					_id:notepad.currentSpeaker,
					firstAccess:true
				});
				console.log("first access for this user");
			} else {
				// Update preferences
				nUserPrefsDB.update({_id:notepad.currentSpeaker}, {firstAccess:false}, {}, function(err,numReplace){});
				console.log("returning user");
			}
		});
	}
	emitter.emit('listen');
    });
}

When we connected to our user’s preference database, we stored that connection object in a variable called nUserPrefsDB. We can now use nUserPrefsDB to search the database (nUserPrefsDB.find()) and update records (nUserPrefsDB.update()).

In this case, if the user is new and the database is empty (i.e. docs.length is 0), we create a new record in the database storing the name as the record ID and a boolean called firstAccess which is set to true for all new users.

If our search in the user’s preference database returns results, we know they’ve been here before and we can update the firstAccess record to false. We also drop in a couple console.log calls so you see this output in real-time.

6) That’s it! This is a very simple example of storing user’s preferences and what you can do with them. A better usage would be to actually act upon this information and have Jibo do or say things differently depending on if the user is new or not. For instance, you may decide not to set your firstAccess boolean to false unless the user has finished listen to some initial instructions for your skill. Or you might ask them questions related to your skill and store their answers in their preference database so that you can customize the skill just for them. To do that, you’ll probably want to read up on the NeDB database to get an idea of how to store and retrieve additional records.

Hope you liked the quick tutorial and feel free to ask any questions if needed.

Download

For the uninspired, or those getting errors, here’s a main.bt file with the user preferences ready to go. Just make sure to have nedb npm installed (per Step #2 above) in your skill to use it without error.
main.bt (7.8 KB)

4 Likes

Great work @michael , this will come in handy :slight_smile:

Thank you, Al. Hopefully this is just one more piece that can help the Jibo development community put together better skills even faster.

1 Like

Hi everyone,

I wanted to pop in and thank you, Michael, for documenting this solution! Very cool. :smile:

I also wanted to let you know that, while a persistent database is not available at the moment, we are buiding out an official Jibo supported method for handling persistent info (like preferences) that will be available in the future. When that is available we will announce it in our documentation.

For the time being you should feel free to utilize this NeDB option as Mike has documented and as I mentioned in one of the forum’s early posts here.

1 Like

Thanks, John. Glad to hear a built-in solution is on it’s way…that should make it super simple to store user preferences and will probably add some pretty powerful connections as well to the skill (e.g. user data-sharing between the system and skills). After that comes out, this little tutorial can serve as a basic how-to for connecting skills to NeDB.

1 Like

Very Very Very cool!! Thank you @michael for writing this up!