Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how to store session data into custom storage in bot builder

I am using node js to build chat bot.

I'm currently storing session data into Microsoft default storage, which have a limit of 64K per user session. I want to use my own storage to store session data. Here is what I got help from Microsoft developer.

I am able to store in document DB and Azure table.

Still, I am confused. How we will implement the IStorageClient interface to store in own DB?

Whenever I set session.UserData.name="" it should store in own DB.

like image 334
Aakash Kag Avatar asked Apr 01 '17 06:04

Aakash Kag


4 Answers

I'm not sure if I got the issue; I understand what you are trying to do and it seems you have all the pieces in place.

First, you have to implement the IStorageClient interface. In your implementation, you write the logic to store things in your DB.

You can review the DocumentDB and Azure Tables implementations to get an idea of how that can be implemented.

Now, the question is.. if your custom storage is in Azure..., then you might be able to use the AzureBotStorage with your custom storage client. You instantiate your custom storage client, pass a reference to the AzureBotStorage and set that as the storage in the bot

// Azure DocumentDb State Store
var docDbClient = new azure.DocumentDbClient({
    host: process.env.DOCUMENT_DB_HOST,
    masterKey: process.env.DOCUMENT_DB_MASTER_KEY,
    database: process.env.DOCUMENT_DB_DATABASE,
    collection: process.env.DOCUMENT_DB_COLLECTION
});
var botStorage = new azure.AzureBotStorage({ gzipData: false }, docDbClient);

// Set Custom Store
bot.set('storage', botStorage);

If your custom storage is in any other place than Azure, then AzureBotStorage might not work for you. Please note that I'm not sure of this, you will have to check the code to confirm. From what I saw, it seems pretty generic, and so you might be able to reuse it and just implement IStorageClient. If that's not the case, you will have to also implement the IBotStorage interface.

At the end, AzureBotStorage is doing that. It implements IBotStorage and uses an IStorageClient to interact with the actual provider.

like image 63
Ezequiel Jadib Avatar answered Oct 17 '22 02:10

Ezequiel Jadib


I'm posting this answer on a very, very old thread just because it seems to be on the top results for implementing custom storage options for Bot Framework.

There's a considerably easier solution that does not involve using npms that have not been updated in a while that carry a risk of not being auditable for corporate environments or that will flood your console with huge amounts of log entries.

The easiest way is to create your own Data Storage code for your bot using the most up-to-date driver for the db of your choice by following the steps in this Undocumented Guide for Custom Data Storage in Microsoft Bot Framework

It took me about an hour to implement for MongoDB, including some learning curve for their newest driver.

In short, in case the link dies at some point, you can create a middleware that exports two key functions that Bot Framework is looking for: getData and saveData. Each of these have to have the following rough structure (from the link using Redis):

var storageKey = 'conversationId';
module.exports = {
getData : (data, callback) => {
    redisClient.get(data[storageKey], (err, reply) => {
        if (err || !reply) {
            callback(err, null);
        } else {
            callback(null, JSON.parse(reply));
        }
    })
},
saveData : (data, value, callback) => {
        redisClient.set(data[storageKey], JSON.stringify(value), (err, reply) => {
            if (err) {
                callback(err);
            } else if (reply === 'OK') {
                callback(null);
            }
        })
    }
}

Then, in your app.js, be sure to require() your middleware and set it accordingly:

const dbMiddleware = require('./path/to/middleware')

bot.set('storage', dbMiddleware)
like image 42
cornopaez Avatar answered Oct 17 '22 03:10

cornopaez


I written code to store bot data into Mongo Db. Full code and details can find in BotBuilder-MongoDB

IstorageClient Interface implementation code:

"use strict";
var Consts = require('./Consts');
var mongodb_1 = require("mongodb");
var replaceDot_Atrate = require("./replaceDot");
var mongoDbConnection = require('./connection.js');
var conf = require('../config');
var conversational_collname = conf.db.conversationalCollection;

var IStorageClient = (function () {

    function IStorageClient(options) {
        this.options = options;
    }

    IStorageClient.prototype.retrieve = function (partitionKey, rowKey, callback) {
        var id = partitionKey + ',' + rowKey;
        if(rowKey!=="userData"){
            var query={"$and":[{"userid":id}]}
                mongoDbConnection(function(err,db) {
                var iterator= db.collection(conversational_collname).find(query);
                iterator.toArray(function (error, result, responseHeaders) {
                    if (error) {
                        console.log("Error",error)
                        callback(error, null, null);
                    }
                    else if (result.length == 0) {
                        callback(null, null, null);
                    }
                    else {
                        var document_1 = result[0];
                        var finaldoc=replaceDot_Atrate.substituteKeyDeep(document_1, /\@/g, '.');
                        finaldoc["id"]=id
                        callback(null, finaldoc, null);
                    }
                });
            }); 
        }
        else{
            var query={"$and":[{"userid":partitionKey}]}
            mongoDbConnection(function(err,db) { 

                var iterator= db.collection(conversational_collname).find(query);
                iterator.toArray(function (error, result, responseHeaders) {
                    if (error) {
                        callback(error, null, null);
                    }
                    else if (result.length == 0) {
                        //console.log("result length 0")
                        callback(null, null, null);
                    }
                    else {
                        var document_1 = result[0];
                        callback(null, document_1, null);
                    }
                });
            });
        }
    };

    IStorageClient.prototype.initialize = function (callback) {
        var _this = this;
        var client=mongodb_1.MongoClient;
        this.client = client;

        mongoDbConnection(function(err,database) {    
                _this.database = database;
                _this.collection = database.collection(conversational_collname);
                callback(null);
         });
    };

    IStorageClient.prototype.insertOrReplace = function (partitionKey, rowKey, entity, isCompressed, callback) {
        var id=partitionKey + ',' + rowKey
        var docDbEntity = { id: partitionKey + ',' + rowKey, data: entity, isCompressed: isCompressed };
        if(rowKey!=="userData"){
            var newEntitiy=replaceDot_Atrate.substituteKeyDeep(entity, /\./g, '@');
            var conditions1 = {
                'userid': id
            };
            var updateobj1 = {
                "$set": {"data":newEntitiy,"isCompressed":false}
            };   
            mongoDbConnection(function(error,db) {    
                db.collection(conversational_collname).update(conditions1,updateobj1,{upsert: true},function(err,res){
                callback(error, null,"");
            });
            });
        }
        else{
            var conditions = {
                'userid': partitionKey
            };
            var update = {
                "$set": {"data":entity}
            }
            mongoDbConnection(function(error,db) {    
                db.collection(conversational_collname).update(conditions,update,{upsert: true},function(err,res){
                callback(error, null,"");
           })
        });
        } 
    };


    IStorageClient.getError = function (error) {
        if (!error)
            return null;
        return new Error('Error Code: ' + error.code + ' Error Body: ' + error.body);
    };

    return IStorageClient;
}());
exports.IStorageClient = IStorageClient;
like image 2
Aakash Kag Avatar answered Oct 17 '22 04:10

Aakash Kag


Here is another take on this library: mongo-bot-storage

import {MongoDbBotStorage, MongoDBStorageClient} from "mongo-bot-storage";

// use it like this 
bot.set("storage", new MongoDbBotStorage(new MongoDBStorageClient({
    url: "mongodb://localhost/mydb",
    mongoOptions: {}
})));

// or like this
MongoClient.connect("mongodb://localhost/mydb", (error, db) => {
    bot.set("storage", new MongoDbBotStorage(new MongoDBStorageClient({db})));
});

and then use the default session.userData, etc..

don't forget to

// Enable Conversation Data persistence
bot.set('persistConversationData', true);
like image 1
Paul Rysevets Avatar answered Oct 17 '22 02:10

Paul Rysevets