Search code examples
c#node.jsbotframeworkazure-table-storagedirect-line-botframework

How to store & retrieve Bot Data in Azure Table storage with directLine channel?


I'm using Microsoft Bot Framework with directLine channel. My Bot is a part of company's customer portal from where I fetch some user information and store it in BotState using stateClient as shown below

 public ActionResult Index()
        {
            var userId = ClaimsPrincipal.Current.FindFirst(ClaimTypes.NameIdentifier).Value;
            GetTokenViaBootStrap().Wait();

            var botCred = new MicrosoftAppCredentials(
              ConfigurationManager.AppSettings["MicrosoftAppId"],
              ConfigurationManager.AppSettings["MicrosoftAppPassword"]);
            var stateClient = new StateClient(botCred);
            BotState botState = new BotState(stateClient);
            BotData botData = new BotData(eTag: "*");
            botData.SetProperty<string>("UserName", result.UserInfo.GivenName + " " + result.UserInfo.FamilyName);
            botData.SetProperty<string>("Email", result.UserInfo.DisplayableId);
            botData.SetProperty<string>("GraphAccessToken", UserAccessToken);
            botData.SetProperty<string>("TokenExpiryTime", result.ExpiresOn.ToString());

            stateClient.BotState.SetUserDataAsync("directline", userId, botData).Wait();

            var UserData = new UserInformationModel
            {
                UserId = userId,
                UserName = result.UserInfo.GivenName + " " + result.UserInfo.FamilyName
            };
            return View(UserData);
        }

As its a directLine channel, I'm connecting my bot using secret in javascript as shown below:

  BotChat.App({
        bot: { id: 'my_bot_id', name: 'my_bot_id' },
        resize: 'detect',
        sendTyping: true,    // defaults to false. set to true to send 'typing' activities to bot (and other users) when user is typing
        user: { id: UserData.UserId},
        directLine: {
            secret: "my_bot_secret"
        }
    }, document.getElementById('my_bot_id'));

I'm accessing user information data in Node js Bot captured in MVC site as shown below:

function sessionUserCapture(session) {

    switch (session.message.address.channelId) {
        case 'skypeforbusiness':
            // some code
            break;
        case 'directline':
               userName= session.userData.UserName;
               userEmail= session.userData.Email;
               //some code
            break;
        case 'slack':
        // some code
    }
}

I referred Microsoft's Save state data from Manage state data for above code and then I used userData available in the session to access this data in my Node.JS Bot.

As the StateClient is Deprecated, I referred this to replace stateclient with Azure Table storage. However, I'm not able to understand how can I store the above data in the Table Storage.

Can anyone suggest any article which I can refer to solve this issue?

My Bot is in NodeJs and the I'm using directLine channel in a C# MVC application.


Solution

  • One option is to use the Microsoft Azure Storage Client Library for .NET, as explained in the answer here: How to retrieve Saved Conversation Data in Azure (Tablelogger) Just make sure to follow the exact same PartitionKey strategy as is followed by the TableBotDataStore class, and serialize the data field correctly.

    -- Edit: I tested this out, and it does in fact work as expected.

     public class WebChatController : Controller
    {
        public ActionResult Index()
        {
            var connectionString = ConfigurationManager.ConnectionStrings["StorageConnectionString"].ConnectionString;
            CloudStorageAccount storageAccount = CloudStorageAccount.Parse(connectionString);
    
            CloudTableClient tableClient = storageAccount.CreateCloudTableClient();
    
            CloudTable table = tableClient.GetTableReference("BotStore");
            string userId = Guid.NewGuid().ToString();
            TableQuery<BotDataRow> query = new TableQuery<BotDataRow>().Where(TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, userId));
    
            var dataRow = table.ExecuteQuery(query).FirstOrDefault();
            if(dataRow != null)
            {
                dataRow.Data = Newtonsoft.Json.JsonConvert.SerializeObject(new
                {
                    UserName = "This user's name",
                    Email = "whatever@email.com",
                    GraphAccessToken = "token",
                    TokenExpiryTime = DateTime.Now.AddHours(1)
                });
                dataRow.Timestamp = DateTimeOffset.UtcNow;
                table.Execute(TableOperation.Replace(dataRow));
            }
            else
            {
                var row = new BotDataRow(userId, "userData");
                row.Data = Newtonsoft.Json.JsonConvert.SerializeObject(new
                {
                    UserName = "This user's name",
                    Email = "whatever@email.com",
                    GraphAccessToken = "token",
                    TokenExpiryTime = DateTime.Now.AddHours(1)
                });
                row.Timestamp = DateTimeOffset.UtcNow;
                table.Execute(TableOperation.Insert(row));
            }
    
            var vm = new WebChatModel();
            vm.UserId = userId;
            return View(vm);
        }
    
        public class BotDataRow : TableEntity
        {
            public BotDataRow(string partitionKey, string rowKey)
            {
                this.PartitionKey = partitionKey;
                this.RowKey = rowKey;
            }
    
            public BotDataRow() { }
    
            public bool IsCompressed { get; set; }
            public string Data { get; set; }
        }
    }
    

    In the node bot:

    'use strict';
    
    const builder = require('botbuilder');
    const restify = require('restify');
    var azure = require('botbuilder-azure');
    
    var tableName = 'BotStore';
    var azureTableClient = new azure.AzureTableClient(tableName,'accountname','accountkey');
    var tableStorage = new azure.AzureBotStorage({ gzipData: false }, azureTableClient);
    
    
    const connector = new builder.ChatConnector({
        appId: process.env.MicrosoftAppId,
        appPassword: process.env.MicrosoftAppPassword
        });
    
    const server = restify.createServer();
    server.listen(process.env.port || process.env.PORT || 3979, () => {
        console.log(`${server.name} listening to ${server.url}`);
    });
    
    server.post('/api/messages', connector.listen());
    
    var bot = new builder.UniversalBot(connector)
        .set('storage', tableStorage);;
    
    bot.dialog('/',
    [
        function (session){
            var data = session.userData;
        }
    ]);
    

    enter image description here