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.
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.
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)
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;
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);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With