I have a bot with the following conversation scenario:
Send text to LUIS
LUIS intent calls context.Call(...) to launch a Dialog
This dialog terminates, save some info in the userData:
private static async Task storeBotData(IDialogContext context, BotData userData)
{
Activity activity = (Activity)context.Activity;
StateClient sc = activity.GetStateClient();
await sc.BotState.SetUserDataAsync(activity.ChannelId, activity.From.Id, userData);
}
And after it call another dialog, again with context.Call(...).
Then the last dialog runs and terminates.
My problem is that when updating the user data at the end of the first dialog (step 3), I have the following exception in the Bot Framework Channel Emulator:
`Exception: The data is changed [File of type 'text/plain']`...
What happens here ? I think that when a dialog terminates, it call setUserData by itself, but I don't understand why I can't update userData anywhere in the code...
I have tried to catch the exception, but nothing is catched.. But I know that the userData is updated, because when I try to retrieve it back, it is updated...
Any help is welcome :)
Thanks
Botframework restores/saves state of conversation after each act of activity, so under the covers typical flow looks like the following:
[23:15:40] <- GET 200 getUserData
[23:15:47] <- GET 200 getConversationData
[23:15:47] <- GET 200 getPrivateConversationData
...
[23:16:42] <- POST 200 setConversationData
[23:16:42] <- POST 200 setUserData
[23:16:42] <- POST 200 setPrivateConversationData
As it is mentioned here : These botData objects will fail to be stored if another instance of your bot has changed the object already. So in your case the exception occurs at the termination of dialog, when framework calls setUserData by himself and figures out that the BotData has been changed already (by your explicit call of BotState.SetUserDataAsync). I suppose that's why you were not able to catch the exception.
Solution:
I used the following code and it fixed the issue:
private static void storeBotData(IDialogContext context, BotData userData)
{
var data = context.UserData;
data.SetValue("field_name", false);
}
The reason it works is that we modify the object of UserData but allow botFramework to "commit" it himself, so there is no conflict
I agree with #Artem (this solved my issue too, thanks!). I would just add the following guideline.
Use
var data = context.UserData;
data.SetValue("field_name", false);
whenever you have a IDialogContext object available, so you let the Bot Framework commit changes.
Use instead
StateClient sc = activity.GetStateClient();
await sc.BotState.SetUserDataAsync(activity.ChannelId, activity.From.Id, userData);
when you don't have an IDialogContext object, e.g. in the MessageController class.
Related
I was able to convert my EchoBot to interact with QnAMaker as per instructions here on my local development system but when I publish the same using kudu repo (tried using Azure DevOps service Ci/CD pipeline but it does not work [in preview] because after deployment the bot just hangs on portal and never able to test it on web chat.. so gave up and used recommended kudu repo), I do not get the correct answer to my response. For every question I send, it is unable to detect the QnAMaker service. And I am returning error message from the code that says no QnaMaker answer was found.
How do I troubleshoot to identify the cause of this?
My bot file seems to be working fine locally and I am able to get the answer from QnAMaker locally but not after publishing the code to my Web App Bot in Azure.
I feel like Botframework V4 (using .net) is not very straight forward and the instruction on the portal (document) is still kind of evolving or sometime incomprehensible.
Here is the snapshot from my emulator while testing the chat locally:
And here is the snapshot of production endpoint (using the same questions on portal) with my error msg from OnTurnAsync function:
My .bot has all the services defined and local bot is working fine.
This is the code in my ChatBot class:
public async Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken = default(CancellationToken))
{
// Handle Message activity type, which is the main activity type for shown within a conversational interface
// Message activities may contain text, speech, interactive cards, and binary or unknown attachments.
// see https://aka.ms/about-bot-activity-message to learn more about the message and other activity types
if (turnContext.Activity.Type == ActivityTypes.Message)
{
// Get the conversation state from the turn context.
var state = await _accessors.CounterState.GetAsync(turnContext, () => new CounterState());
// Bump the turn count for this conversation.
state.TurnCount++;
// Set the property using the accessor.
await _accessors.CounterState.SetAsync(turnContext, state);
// Save the new turn count into the conversation state.
await _accessors.ConversationState.SaveChangesAsync(turnContext);
// Echo back to the user whatever they typed.
//var responseMessage = $"Turn {state.TurnCount}: You sent '{turnContext.Activity.Text}'\n";
//await turnContext.SendActivityAsync(responseMessage);
// QnAService
foreach(var qnaService in _qnaServices)
{
var response = await qnaService.GetAnswersAsync(turnContext);
if (response != null && response.Length > 0)
{
await turnContext.SendActivityAsync(
response[0].Answer,
cancellationToken: cancellationToken);
return;
}
}
var msg = "No QnA Maker answers were found. Something went wrong...!!";
await turnContext.SendActivityAsync(msg, cancellationToken: cancellationToken);
}
else
{
await turnContext.SendActivityAsync($"{turnContext.Activity.Type} event detected");
}
}
I am try to save data to the MemoryStorage in Microsoft Bot Frame Work (in .NET environment).
I am using this method for do it:
public static class StateManager
{
private static MemoryStorage _myStorage;
static StateManager()
{
_myStorage = new MemoryStorage();
}
public async static void Save(UserDetails userDetails)
{
var changes = new Dictionary<string, object>();
{
changes.Add("ud", userDetails);
}
await _myStorage.WriteAsync(changes, new CancellationToken());
}
}
until now it's always work fine. but suddenly i am getting this error:
System.Exception: Etag conflict. Original: 4 Current: 5
any idea how to solve this error? thanks!
edit - with solve
I got that the problem was that i push data to the memory twice in a row (without get the data between the tow pushes). it's mean that after i push data one time, i have to get the data from the storage before i push the data again.
My question now it's why? i cannot save data twice without get the data between the tow pushes?
Without more code, I wasn't able to replicate your issue. However, it sounds like you have a concurrency problem.
Your Save() method returns a void. You should instead use:
public async static Task Save(UserDetails userDetails)
Then, when saving, call with:
await StateManager.Save(userDetails).
However, you can save yourself the trouble of these kinds of things and use BotBuilder's built-in state storage. References:
Save User and Conversation Data
Core Bot Sample - This is an example of good user profile storage
I've been programming a lot with the MS Bot Framework but I can't seem to make this seemingly trivial part work: sending a Typing activity.
This fails every time:
"Object reference not set to an instance of an object"
What is going on?
Try it like this (if inside a dialog):
private async Task MessageReceivedAsync(IDialogContext context, IAwaitable<object> result)
{
var activity = await result as Activity;
var activity2 = activity.CreateReply();
activity2.Type = ActivityTypes.Typing;
await context.PostAsync(activity2);
context.Wait(MessageReceivedAsync);
}
This is most likely because you are using MakeMessage() but not actually setting any of the fields it needs, you are only setting Type so some property in the activity created is throwing the null object reference error.
I am digging through all the great new stuff in v3 of the bot framework. One of the things that I am trying to do is create a dialog that responds with cards. But I cannot find a sample that shows how to do this.
I've tried to monkey with it on my own but haven't had much luck. In most of their code samples for Dialogs you cast the Activity object you get in your Post to an IMessageActivity class. Then when you respond you do so with just plain text. All the examples with cards have you create an Activity class. However because I've cast it to IMessageActivity I can't use the CreateReply method. And if I can't do that then when I create an Activity I get an error that the 'activityId' cannot be null.
Any advice, thoughts, or insight would be greatly appreciated.
Thanks in advance!
I added this code to my dialog:
protected override async Task MessageReceived(IDialogContext context, IAwaitable<IMessageActivity> item)
{
_message = (Activity)await item;
await base.MessageReceived(context, item);
}
[field: NonSerialized()]
private Activity _message;
[LuisIntent("Ping")]
public async Task Ping(IDialogContext context, LuisResult result)
{
Activity replyToConversation = _message.CreateReply("Should go to conversation, with a carousel");
replyToConversation.Recipient = _message.From;
replyToConversation.Type = "message";
replyToConversation.AttachmentLayout = "carousel";
.
.
.
await context.PostAsync(replyToConversation);
context.Wait(MessageReceived);
}
I got it working in the emulator but not in Skype but I guess my problem is this one Rich Card attachments are not showing on web chat or Skype
We are trying to test push notifications, using the latest code from the documentation How to: Set Up a Notification Channel for Windows Phone
public HttpNotificationChannel myChannel;
public void CreatingANotificationChannel()
{
myChannel = HttpNotificationChannel.Find("MyChannel");
if (myChannel == null)
{
myChannel = new HttpNotificationChannel("MyChannel","www.contoso.com");
// An application is expected to send its notification channel URI to its corresponding web service each time it launches.
// The notification channel URI is not guaranteed to be the same as the last time the application ran.
myChannel.ChannelUriUpdated += new EventHandler<NotificationChannelUriEventArgs>(myChannel_ChannelUriUpdated);
myChannel.Open();
}
else // Found an existing notification channel.
{
// The URI that the application sends to its web service.
Debug.WriteLine("Notification channel URI:" + myChannel.ChannelUri.ToString());
}
myChannel.HttpNotificationReceived += new EventHandler<HttpNotificationEventArgs>(myChannel_HttpNotificationReceived);
myChannel.ShellToastNotificationReceived += new EventHandler<NotificationEventArgs>(myChannel_ShellToastNotificationReceived);
myChannel.ErrorOccurred += new EventHandler<NotificationChannelErrorEventArgs>(myChannel_ErrorOccurred);
}
If HttpNotificationChannel.Find() returns null, it opens a new channel, but the ChannelUriUpdated event is never triggered.
If HttpNotificationChannel.Find() returns a channel, the ChannelUri property is null. The sample code crashes here because it assumes the ChannelUri property to be not null.
In neither case is the ErrorOccurred event triggered.
How can i solve this problem? This problem is because of microsoft server or any thing else?
Thnks in advance
EDIT
Waiting for replay,after ten days i am suffering of null uri problem
Can any one tell me how can i solve this problem some time MSPN server give chanalk uri ans some time not i mean some time it give null reference Exception.
What Microsoft doing?
If I don't go wrong, www.contoso.com it's a example URI to demonstrate that you need to put your own server URL address, but in my experience, I never use in that way. I prefer just to put
myChannel = new HttpNotificationChannel("MyChannel");
Look this example (it's in Spanish) but the codes are very clear of what you need to do to set the push notification client and service.
I hope I helped you.
You are testing in what mobile are Emulator,
Do you have developer account subscription for windows phone development,
Had you Developer unlocked your mobile,
Noorul.
I think the problem is that you are using the HttpNotificationChannel constructor of the authenticated web service, according to the documentation.
Instead, you should use the constructor that takes only one parameter, as you can check in this example
/// Holds the push channel that is created or found.
HttpNotificationChannel pushChannel;
// The name of our push channel.
string channelName = "ToastSampleChannel";
// Try to find the push channel.
pushChannel = HttpNotificationChannel.Find(channelName);
// If the channel was not found, then create a new connection to the push service.
if (pushChannel == null)
{
pushChannel = new HttpNotificationChannel(channelName);
...
}
Hope it helps