问题
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"));
}
}
}
}
回答1:
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:
- Write directly to Storage-Cosmos
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:
Conversation History Sample
Transcript Storage Docs
Just read the docs and look at the sample for this one. Too much code to copy/paste.
Saving Custom Data in V3
References:
Manage custom data storage
BotState Class Reference
Using Azure Table Storage with Cosmos
Sample showing how to store UserData
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:
Blog post for saving conversation history to a SQL Server
Sample that uses Middleware to log all activity
Intercept messages docs
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);
}
}
来源:https://stackoverflow.com/questions/55241179/save-conversation-history-from-ms-bot-to-cosmos-db