Как хранить и извлекать данные бота в хранилище таблиц Azure по каналу directLine?

Я использую Microsoft Bot Framework с directLine канал. Мой бот является частью клиентского портала компании, откуда я беру некоторую информацию о пользователе и сохраняю ее в BotState с помощью stateClient как показано ниже

 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);
        }

Как его directLine канал, я подключаю свой бот, используя секрет в JavaScript, как показано ниже:

  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'));

Я получаю доступ к данным о пользователях в Node js Bot, захваченных на сайте MVC, как показано ниже:

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
    }
}

Я сослался на данные о состоянии сохранения Microsoft из данных управления состоянием для приведенного выше кода, а затем использовал userData доступны в session чтобы получить доступ к этим данным в моем Node.JS Bot.

Поскольку StateClient устарел, я сослался на это, чтобы заменить stateclient с Azure Table storage, Тем не менее, я не могу понять, как я могу сохранить вышеуказанные данные в Table Storage,

Кто-нибудь может предложить какую-нибудь статью, к которой я могу обратиться, чтобы решить эту проблему?

Мой бот находится в NodeJs а я использую directLine канал в C# MVC application,

3 ответа

Решение

Один из вариантов - использовать клиентскую библиотеку хранилища Microsoft Azure для.NET, как описано в ответе ниже: Как извлечь сохраненные данные разговора в Azure (Tablelogger). Просто следуйте той же стратегии PartitionKey, что и класс TableBotDataStore. и правильно сериализовать поле данных.

- Изменить: я проверил это, и это на самом деле работает, как ожидалось.

 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; }
    }
}

В узле бота:

'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;
    }
]);

Код, который вы используете, использует устаревшее состояние по умолчанию и не будет работать. Чтобы добиться того, чего вы хотите, это зависит от того, где вы находитесь в своем коде. Решающим фактором является, если у вас есть доступ к context объект или нет.

Например, если вы находитесь в MessagesController у вас не будет доступа к объекту контекста, и ваш код может выглядеть так:

public async Task<HttpResponseMessage> Post([FromBody]Activity activity)
            {
                if (activity.Type == ActivityTypes.Message)
                {

                    var message = activity as IMessageActivity;
                    using (var scope = DialogModule.BeginLifetimeScope(Conversation.Container, message))
                    {
                        var botDataStore = scope.Resolve<IBotDataStore<BotData>>();
                        var key = Address.FromActivity(message);

                        var userData = await botDataStore.LoadAsync(key, BotStoreType.BotUserData, CancellationToken.None);

                        userData.SetProperty("key 1", "value1");
                        userData.SetProperty("key 2", "value2");

                        await botDataStore.SaveAsync(key, BotStoreType.BotUserData, userData, CancellationToken.None);
                        await botDataStore.FlushAsync(key, CancellationToken.None);
                    }
                    await Conversation.SendAsync(activity, () => new Dialogs.RootDialog());
                }
            } 

Затем, чтобы получить данные:

userData.GetProperty<string>("key 1");

Другая ситуация была бы, если бы у вас был доступ к объекту контекста, как в Dialog например, ваш код может выглядеть так:

        context.UserData.SetValue("key 1", "value1");
        context.UserData.SetValue("key 2", "value2");

Затем, чтобы получить данные:

context.UserData.GetValue<string>("key 1");

Вы настроили своего бота для подключения к хранилищу таблиц Azure в Global.asax.cs вместо устаревшего состояния по умолчанию?
Я написал сообщение в блоге с полной информацией о том, как перенести состояние вашего бота в хранилище таблиц Azure, которое вы должны просмотреть здесь.

protected void Application_Start()
    {
        Conversation.UpdateContainer(
            builder =>
            {
                builder.RegisterModule(new AzureModule(Assembly.GetExecutingAssembly()));

                // Using Azure Table for storage
                var store = new TableBotDataStore(ConfigurationManager.ConnectionStrings["StorageConnectionString"].ConnectionString);

                builder.Register(c => store)
                    .Keyed<IBotDataStore<BotData>>(AzureModule.Key_DataStore)
                    .AsSelf()
                    .SingleInstance();
            });

        GlobalConfiguration.Configure(WebApiConfig.Register);
    }
Другие вопросы по тегам