0

The bot I'm developing is a replacement for a contact form for potential clients that want to be contacted by a company, so the user inputs have to be saved in a database. I have successfully connected a Cosmos DB to my bot which collect the state data when the bot is used. I have a dialog stack with one dialog per user input (Name, email and the message the user want to leave).

I can't find any helpful documentation on how to save conversation history for bots written in C#. Can anyone help me out? I'm still a beginner in Bot Framework and C#.

Here is my global.asax file:

 public class WebApiApplication : System.Web.HttpApplication
{
    protected void Application_Start()
    {
        GlobalConfiguration.Configure(WebApiConfig.Register);
        var uri = new Uri(ConfigurationManager.AppSettings["DocumentDbUrl"]);
        var key = ConfigurationManager.AppSettings["DocumentDbKey"];
        var store = new DocumentDbBotDataStore(uri, key);

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

                        builder.Register(c => new CachingBotDataStore(store, CachingBotDataStoreConsistencyPolicy.ETagBasedConsistency))
                            .As<IBotDataStore<BotData>>()
                            .AsSelf()
                            .InstancePerLifetimeScope();

                    });

    }
}

Here is my NameDialog to collect the user's name: (the other dialogs are almost identical to this)

[Serializable]
public class NameDialog : IDialog<string>
{
    private int attempts = 3;

    public async Task StartAsync(IDialogContext context)
    {
        await context.PostAsync("What's your name?");

        context.Wait(this.MessageReceivedAsync);
    }

    private async Task MessageReceivedAsync(IDialogContext context, IAwaitable<IMessageActivity> result)
    {
        var message = await result;


        if ((message.Text != null) && (message.Text.Trim().Length > 0))
        {

            context.Done(message.Text);
        }

        else
        {
            --attempts;
            if (attempts > 0)
            {
                await context.PostAsync("I couldn't understand, can you try again?");

                context.Wait(this.MessageReceivedAsync);
            }
            else
            {

                context.Fail(new TooManyAttemptsException("This is not a valid input"));
            }
        }
    }
}
Charlotte6789
  • 53
  • 1
  • 8

1 Answers1

2

I submitted a couple of comments asking for clarification in what you're looking for, but figured I may as well just provide an all-encompassing answer.

Use V4

If your bot is new, just use V4 of BotBuilder/BotFramework. It's easier, there's more features, and better support. I'll provide answers for both, anyway.

Saving Custom Data in V4

References:

For custom storage where you specify the User Id:

// Create Cosmos Storage
private static readonly CosmosDbStorage _myStorage = new CosmosDbStorage(new CosmosDbStorageOptions
{
   AuthKey = CosmosDBKey,
   CollectionId = CosmosDBCollectionName,
   CosmosDBEndpoint = new Uri(CosmosServiceEndpoint),
   DatabaseId = CosmosDBDatabaseName,
});

// Write
var userData = new { Name = "xyz", Email = "xyz@email.com", Message = "my message" };
var changes = Dictionary<string, object>();
{
    changes.Add("UserId", userData);
};
await _myStorage.WriteAsync(changes, cancellationToken);

// Read
var userDataFromStorage = await _myStorage.read(["UserId"]);

For User Data where the bot handles the Id:

See Basic Bot Sample.

Key parts:

Define the Greeting State

public class GreetingState
{
    public string Name { get; set; }

    public string City { get; set; }
}

Instantiate a State Accessor

private readonly IStatePropertyAccessor<GreetingState> _greetingStateAccessor;
[...]
_greetingStateAccessor = _userState.CreateProperty<GreetingState>(nameof(GreetingState));
[...]
Dialogs.Add(new GreetingDialog(_greetingStateAccessor));

Save UserState at the end of OnTurnAsync:

await _userState.SaveChangesAsync(turnContext);

Greeting Dialog to Get and Set User Data

var greetingState = await UserProfileAccessor.GetAsync(stepContext.Context, () => null);
[...]
greetingState.Name = char.ToUpper(lowerCaseName[0]) + lowerCaseName.Substring(1);
await UserProfileAccessor.SetAsync(stepContext.Context, greetingState);

Saving Full Conversation History in V4

References:

Just read the docs and look at the sample for this one. Too much code to copy/paste.

Saving Custom Data in V3

References:

I'll copy/paste the code from this good answer to a similar question on StackOverflow, for posterity:

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

Saving User Data:

See State API Bot Sample

Saving Full Conversation History in V3

References:

Basically, you want to first capture all activity using IActivityLogger, like in the sample just above:

Create DebugActivityLogger

public class DebugActivityLogger : IActivityLogger
{
    public async Task LogAsync(IActivity activity)
    {
        Debug.WriteLine($"From:{activity.From.Id} - To:{activity.Recipient.Id} - Message:{activity.AsMessageActivity()?.Text}");
        // Add code to save in whatever format you'd like using "Saving Custom Data in V3" section
    }
}

Add the following to Global.asax.cs:

public class WebApiApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        {
            var builder = new ContainerBuilder();
            builder.RegisterType<DebugActivityLogger>().AsImplementedInterfaces().InstancePerDependency();
            builder.Update(Conversation.Container);

            GlobalConfiguration.Configure(WebApiConfig.Register);
        }
    }
mdrichardson
  • 7,141
  • 1
  • 7
  • 21
  • your link "Conversation History Sample" for storing full conversation is not working. Could you please post the updated link? – Rohan Rao Dec 28 '19 at 05:57
  • @RohanRao We no longer provide an updated sample for this, but [here's the old one](https://github.com/microsoft/BotBuilder-Samples/tree/8cb91cc83a53fd471637d4d6241baa89ffd76625/samples/csharp_dotnetcore/22.conversation-history) – mdrichardson Dec 30 '19 at 18:24