couldn't send retry but the message has been sent - botframework

In the bot web chat, when I type a message, the bot says "sending" first and then changes to "couldn't send, Retry". But the message is sent and I am getting the reply. How can I avoid this? Do I need to increase the message timeout? If so, where I need to set it?
This is the code snippet. I am using C# SDK where I have coded in MessageReceivedASync method
namespace Bot_Application1.Dialogs
{
public class HRBotDialog : IDialog<object>
{
public static string dialogcontext = "";
public async Task StartAsync(IDialogContext context)
{
context.Wait(MessageReceivedAsync);
}
private async Task MessageReceivedAsync(IDialogContext context, IAwaitable<IMessageActivity> argument)
{
// Get the text passed
var message = await argument;
string typedText = message.Text.ToLower();
string answer = "My Answer";
if ((typedText == "hi") || (typedText == "hello"))
{
answer = message.Text;
}
else if ((typedText == "how many personal days do i have left") || (typedText.Contains("personal days")))
{
answer = "Looks like you have 2 remaining for this year";
}
I am adding the controller code here
//[BotAuthentication]
public class MessagesController : ApiController
{
/// <summary>
/// POST: api/Messages
/// Receive a message from a user and reply to it
/// </summary>
public async Task<HttpResponseMessage> Post([FromBody]Activity activity)
{
if (activity.Type == ActivityTypes.Message)
{
await Conversation.SendAsync(activity, () => new HRBotDialog());
}
else
{
ConnectorClient connector = new ConnectorClient(new Uri(activity.ServiceUrl));
var reply = HandleSystemMessage(activity);
if (reply != null)
await connector.Conversations.ReplyToActivityAsync(reply);
}
var response = Request.CreateResponse(HttpStatusCode.OK);
return response;
}
private Activity HandleSystemMessage(Activity message)
{
if (message.Type == ActivityTypes.DeleteUserData)
{
// Implement user deletion here
// If we handle user deletion, return a real message
}
else if (message.Type == ActivityTypes.ConversationUpdate)
{
string replyMessage = string.Empty;
if (message.MembersAdded.Any(o => o.Id == message.Recipient.Id))
{
replyMessage += $"How can I help you? \n";
return message.CreateReply(replyMessage);
}
}
else if (message.Type == ActivityTypes.ContactRelationUpdate)
{
// Handle add/remove from contact lists
// Activity.From + Activity.Action represent what happened
}
else if (message.Type == ActivityTypes.Typing)
{
// Handle knowing tha the user is typing
}
else if (message.Type == ActivityTypes.Ping)
{
}
return null;
}
}

Please try adding the Serializable attribute to your HRBotDialog like this:
[Serializable]
public class HRBotDialog : IDialog<object>
{
//...code...
}

Related

Dialog stack doesn't reset

I have a scorable dialog that will check if the incoming message is equal to "resetconversation". When true, the dialog resets the dialog stack in the PostAsync method.
When the postasync method is hit, the dialog stack resets as expected but when I again send a message the stack reappears and the conversation proceeds as if it was never reset.
[note]: I have noticed that this issue happens when a PromptDialog.Choice() is expecting an input.
Below is the scorable registration and the scorable dialog class.
Thanks!
Registering my scorable dialog:
Conversation.UpdateContainer(
builder =>
{
builder.RegisterModule(new AzureModule(Assembly.GetExecutingAssembly()));
string conn = ConfigurationManager.ConnectionStrings["BotDataContextConnectionString"].ConnectionString;
var store = new SqlBotDataStore(conn);
builder.Register(c => store)
.Keyed<IBotDataStore<BotData>>(AzureModule.Key_DataStore)
.AsSelf()
.SingleInstance();
//Scorable to check if dialog stack reset has been requested
builder.RegisterType<HandleResetRequestScorable>()
.AsSelf()
.As<IScorable<IActivity, double>>();
});
Scorable Dialog:
[Serializable]
public class HandleResetRequestScorable : ScorableBase<IActivity, bool, double>
{
private readonly IBotToUser _botToUser;
private readonly IDialogStack _dialogStack;
private bool _isMessageType { get; set; }
public HandleResetRequestScorable(IBotToUser botToUser, IDialogStack dialogTask)
{
SetField.NotNull(out _botToUser, nameof(_botToUser), botToUser);
//SetField.NotNull(out _dialogStack, nameof(_dialogStack), dialogTask);
}
protected override async Task<bool> PrepareAsync(IActivity activity, CancellationToken token)
{
string message = string.Empty;
if (activity.Type == ActivityTypes.Event)
{
_isMessageType = false;
message = activity.AsEventActivity().Name.ToLower();
}
else
{
_isMessageType = true;
message = activity.AsMessageActivity().Text.ToLower();
}
if (message == "resetconversation")
return true;
return false;
}
protected override bool HasScore(IActivity item, bool state)
{
return state;
}
protected override double GetScore(IActivity item, bool state)
{
return state ? 1.0 : 0;
}
protected override async Task PostAsync(IActivity activity, bool state, CancellationToken token)
{
try
{
var message = activity as IMessageActivity;
string[] data = message.Value.ToString().Split('|');
using (var scope = DialogModule.BeginLifetimeScope(Conversation.Container, activity as Activity))
{
//get the conversation related data
var botData = scope.Resolve<IBotData>();
await botData.LoadAsync(default(CancellationToken));
//get the dialog stack
var convoStack = scope.Resolve<IDialogStack>();
convoStack.Reset();
if (_isMessageType)
{
await _botToUser.PostAsync("Conversation reset complete.");
await _botToUser.PostAsync("Hi");
}
await botData.FlushAsync(default(CancellationToken));
}
}
catch
{
throw;
}
}
protected override Task DoneAsync(IActivity item, bool state, CancellationToken token)
{
//throw new NotImplementedException();
return Task.CompletedTask;
}
}
}

How to flag that an exception has been handled in bot code?

I have a root dialog that creates a child dialog like so...
private async Task MessageReceivedAsync(IDialogContext context, IAwaitable<IMessageActivity> result)
{
var message = await result;
string userName = context.Activity?.From.Name;
var customerForm = new FormDialog<CarValuationDialog>(
new CarValuationDialog(userName),
() => CarValuationDialog.BuildForm(),
FormOptions.PromptInStart);
context.Call(customerForm, FormSubmitted);
}
The FormSubmitted method looks like....
public async Task FormSubmitted(IDialogContext context, IAwaitable<CarValuationDialog> result)
{
try
{
var form = await result;
}
catch (FormCanceledException<CarValuationDialog> e)
{
string reply;
if (e.InnerException == null)
{
reply = e.Message;
}
else
{
reply = e.InnerException.Message;
}
context.Reset();
await context.PostAsync(reply);
}
}
When an exception occurs in the child dialog, the method FormSubmitted is executed and goes into the catch block. However, when that method finishes, I still see the "Sorry my bot had an issue" type message appear to the user.
How can I tell the bot code not to fire the unhandled exception code, I believe is in PostUnhandledExceptionToUser? Is there a flag type property I need to set to true or something?
It looks like you are making the dialog stack empty when you have an exception: you should not have this context.Reset() below:
public async Task FormSubmitted(IDialogContext context, IAwaitable<CarValuationDialog> result)
{
try
{
var form = await result;
}
catch (FormCanceledException<CarValuationDialog> e)
{
string reply;
if (e.InnerException == null)
{
reply = e.Message;
}
else
{
reply = e.InnerException.Message;
}
context.Reset();
await context.PostAsync(reply);
}
}
Remove this line and the next message will be handled by your root dialog
in your message controller in POST method, use defaultifexception
await Conversation.SendAsync(activity, () => new Dialogs.DialogLUIS().DefaultIfException());

Nested dialogs: message being sent twice

I have a bot where I am doing something like this:
1) A new Activity (message) arrives.
2) I dispatch the message to a RootDialog.
3) Depending on some Logic, RootDialog can:
a) Call a LuisDialog (handling natural language)
b) Call a CustomDialog (handles some business logic).
But when the user state is reset, and the flow leads to an intention inside the LuisDialog, it calls the intention method twice. Just the first time the state is empty, then it works fine.
Let me show you the code:
MessagesController:
public async Task<HttpResponseMessage> Post([FromBody]Activity activity)
{
if (activity.Type == ActivityTypes.Message)
{
ConnectorClient connector = new ConnectorClient(new Uri(activity.ServiceUrl));
try
{
await Conversation.SendAsync(activity, () => new RootDialog());
}
catch (HttpRequestException e)
{
...
}
}
RootDialog:
public class RootDialog : IDialog<object>
{
public async Task StartAsync(IDialogContext context)
{
await MessageReceivedAsync(context, Awaitable.FromItem(context.Activity.AsMessageActivity()));
}
private async Task MessageReceivedAsync(IDialogContext context, IAwaitable<IMessageActivity> awaitable)
{
bool value = DoSomeCustomLogic();
if (value)
{
string message = DoSomething();
await context.PostAsync(message);
} else {
bool value2 = DoSomeCustomLogic2();
if (value2)
{
var answerValidationDialog = new ValidateAnswerWithUserDialog();
context.Call(answerValidationDialog, ValidateAnswerWithUserDialogCompletedCallback);
} else {
var luisDialog = new LuisDialog();
await context.Forward(luisDialog,LuisDialogCompletedCallback, context.Activity, CancellationToken.None);
}
}
}
Callbacks only do context.Done(true);
And LuisDialog has an Intention which goes like this:
[LuisIntent(LuisUtils.INTENT_MENU_SALUTE)]
public async Task SaluteOrMenu(IDialogContext context, LuisResult result)
{
if (LuisUtils.IntentScoreIsHighEnough(result))
{
string userName = context.Activity.From.Name;
ContextHelper helper = new ContextHelper(MessageReceived);
await helper.AskUserToDoSomeInitialAction(context, saluteWord, userName);
context.Done(true);
}
else
{
await None(context, result);
}
}
And finally class ContextHelper:
public class ContextHelper
{
private Func<IDialogContext, IAwaitable<IMessageActivity>, Task> MessageReceived;
public ContextHelper(Func<IDialogContext, IAwaitable<IMessageActivity>, Task> messageReceived)
{
MessageReceived = messageReceived;
}
public async Task AskUserToDoSomeInitialAction(IDialogContext context, string saluteWord, string userName)
{
var reply = context.MakeMessage();
List<CardAction> buttons = BuildInitialOptionActions();
List<CardImage> images = BuildInitialOptionImages();
string initialText = $"Hi stranger!"
var card = new HeroCard
{
Title = "Hello!"
Text = initialText,
Buttons = buttons,
Images = images
};
reply.Attachments = new List<Attachment> { card.ToAttachment() };
await context.PostAsync(reply);
context.Wait(AfterUserChoseOptionInSalute);
}
private async Task AfterUserChoseOptionInSalute(IDialogContext context, IAwaitable<IMessageActivity> result)
{
await ReDispatchMessageReceivedToDialog(context);
}
private async Task ReDispatchMessageReceivedToDialog(IDialogContext context)
{
await MessageReceived(context, Awaitable.FromItem(context.Activity.AsMessageActivity()));
}
}
The SaluteOrMenu Intention gets called twice (only the first time I interact with the bot or when I delete the state. After Debugging I saw that after doing context.Wait(AfterUserChoseOptionInSalute);, the bot calls that function (instead of waiting for an event to call it)
Any ideas?
Thanks in advance.
I found the line that was wrong. It was on the first dialog (the RootDialog):
public async Task StartAsync(IDialogContext context)
{
await MessageReceivedAsync(context, Awaitable.FromItem(context.Activity.AsMessageActivity()));
}
That is a line that re dispatches a message with the incoming activity. I had it somewhere in some chunk of code and (don't know why), thought it was a good idea to use it on StartAsync. So, because of that, two calls were occurring.
Dumb me.
I just changed it to this and worked:
public async Task StartAsync(IDialogContext context)
{
context.Wait(MessageReceivedAsync);
}

show welcome message in Microsoft teams using Microsoft bot framework

I have used below code for showing a welcome message to user.
private Activity HandleSystemMessage(Activity message)
{
if (message.Type == ActivityTypes.DeleteUserData)
{
// Implement user deletion here
// If we handle user deletion, return a real message
}
else if (message.Type == ActivityTypes.ConversationUpdate)
{
string replyMessage = string.Empty;
replyMessage = Responses.Greeting;
return message.CreateReply(replyMessage);
}
else if (message.Type == ActivityTypes.ContactRelationUpdate)
{
// Handle add/remove from contact lists
// Activity.From + Activity.Action represent what happened
}
else if (message.Type == ActivityTypes.Typing)
{
// Handle knowing tha the user is typing
}
else if (message.Type == ActivityTypes.Ping)
{
}
return null;
}
Below method is used to call HandleSystemMessage, if activity type is not message.
public async Task<HttpResponseMessage> Post([FromBody]Activity activity)
{
string reply = "";
ConnectorClient connector = new ConnectorClient(new Uri(activity.ServiceUrl));
if (activity.Type == ActivityTypes.Message)
{
stLuis = await LuisHelper.ParseUserInput(activity.Text);
string userResponse = activity.Text.ToLower();
switch (stLuis.topScoringIntent.intent)
{
case "Greetings":
reply = Responses.Greeting;
break;
case "None":
reply = Responses.None;
break;
default:
break;
}
}
if (reply != "")
await connector.Conversations.ReplyToActivityAsync(activity.CreateReply(reply));
}
else
{
var reply1 = HandleSystemMessage(activity);
if (reply1 != null)
await connector.Conversations.ReplyToActivityAsync(reply1);
}
var response = Request.CreateResponse(HttpStatusCode.OK);
return response;
}
This code works with Skype. But when I add same bot in Microsoft teams, it doesn't show a welcome message.
By now (2016-12-30) MSFT Teams does not send any message at all when you add a bot to "contact list". This is a known limitation and to be addressed in the nearest future, as MSFT guys say.
In the meantime to get a ConversationUpdate message to the bot, the user will have to first initiate a conversation with the bot.
As a workaround you can handle special text sent from user, like "start", or just the very first incoming message, if your bot is stateful enough.

E-Mail spammed while testing an e-mail chatbot

I have created a simple stock bot from the video. I have registered the bot for Skype, Facebook Messenger and E-Mail.
Skype, Facebook messenger works great and replies to the messages are received.
The problem which I have is with the E-Mail. When I send a E-Mail to the bot, for example, "Stock Price of MSFT" it sends an reply e-mail with the stock price and then it sends the same mail again and again without stopping until i remove the e-mail from the dev.botframework.com. Has anyone faced this issue. Is there any additional configuration which is needed to avoid this.
Eg: I sent a message to the bot e-mail saying "What is the stock price of msft" ?
I get a reply back with "Stock Price of msft is ... " from the bot. And the same reply e-mail I am getting continuously until I disable the E-MAIL Bot service..
MessageControl.cs:
using System;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using System.Web.Http;
using System.Web.Http.Description;
using Microsoft.Bot.Connector;
using Newtonsoft.Json;
namespace StockBot2
{
[BotAuthentication]
public class MessagesController : ApiController
{
/// <summary>
/// POST: api/Messages
/// Receive a message from a user and reply to it
/// </summary>
public async Task<HttpResponseMessage> Post([FromBody]Activity activity)
{
if (activity.Type == ActivityTypes.Message)
{
ConnectorClient connector = new ConnectorClient(new Uri(activity.ServiceUrl));
string StockRateString;
StockLUIS StLUIS = await GetEntityFromLUIS(activity.Text);
if (StLUIS.intents.Count() > 0)
{
switch (StLUIS.intents[0].intent)
{
case "StockPrice":
StockRateString = await GetStock(StLUIS.entities[0].entity);
break;
case "StockPrice2":
StockRateString = await GetStock(StLUIS.entities[0].entity);
break;
default:
StockRateString = "Sorry, I am not getting you...";
break;
}
}
else
{
StockRateString = "Sorry, I am not getting you...";
}
// return our reply to the user
Activity reply = activity.CreateReply(StockRateString);
await connector.Conversations.ReplyToActivityAsync(reply);
}
else
{
HandleSystemMessage(activity);
}
var response = Request.CreateResponse(HttpStatusCode.OK);
return response;
}
private Activity HandleSystemMessage(Activity message)
{
if (message.Type == ActivityTypes.DeleteUserData)
{
// Implement user deletion here
// If we handle user deletion, return a real message
}
else if (message.Type == ActivityTypes.ConversationUpdate)
{
// Handle conversation state changes, like members being added and removed
// Use Activity.MembersAdded and Activity.MembersRemoved and Activity.Action for info
// Not available in all channels
}
else if (message.Type == ActivityTypes.ContactRelationUpdate)
{
// Handle add/remove from contact lists
// Activity.From + Activity.Action represent what happened
}
else if (message.Type == ActivityTypes.Typing)
{
// Handle knowing tha the user is typing
}
else if (message.Type == ActivityTypes.Ping)
{
}
return null;
}
private async Task<string> GetStock(string StockSymbol)
{
double? dblStockValue = await YahooBot.GetStockRateAsync(StockSymbol);
if (dblStockValue == null)
{
return string.Format("This \"{0}\" is not an valid stock symbol", StockSymbol);
}
else
{
return string.Format("Stock Price of {0} is {1}", StockSymbol, dblStockValue);
}
}
private static async Task<StockLUIS> GetEntityFromLUIS(string Query)
{
Query = Uri.EscapeDataString(Query);
StockLUIS Data = new StockLUIS();
using (HttpClient client = new HttpClient())
{
string RequestURI = "https://api.projectoxford.ai/luis/v1/application?id=1c85d9de-7c8d-4ceb-ba11-860f02ce911b&subscription-key=fe48030063c6458587138c4dcd258737&q=" + Query;
HttpResponseMessage msg = await client.GetAsync(RequestURI);
if (msg.IsSuccessStatusCode)
{
var JsonDataResponse = await msg.Content.ReadAsStringAsync();
Data = JsonConvert.DeserializeObject<StockLUIS>(JsonDataResponse);
}
}
return Data;
}
}
}
YahooBot.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using System.Web;
namespace StockBot2
{
public class YahooBot
{
public static async Task<double?> GetStockRateAsync(string StockSymbol)
{
try
{
string ServiceURL = $"http://finance.yahoo.com/d/quotes.csv?s={StockSymbol}&f=sl1d1nd";
string ResultInCSV;
using (WebClient client = new WebClient())
{
ResultInCSV = await client.DownloadStringTaskAsync(ServiceURL).ConfigureAwait(false);
}
var FirstLine = ResultInCSV.Split('\n')[0];
var Price = FirstLine.Split(',')[1];
if (Price != null && Price.Length >= 0)
{
double result;
if (double.TryParse(Price, out result))
{
return result;
}
}
return null;
}
catch (WebException ex)
{
//handle your exception here
throw ex;
}
}
}
}

Resources