Using ResumeAfter in proactive bot dialog - botframework

I have a couple of questions about the example that shows how to start a proactive dialog:
using (var scope = DialogModule.BeginLifetimeScope(Conversation.Container, activity))
{
var botData = scope.Resolve<IBotData>();
await botData.LoadAsync(token);
var task = scope.Resolve<IDialogTask>();
var interruption = dialog.Void<T, IMessageActivity>();
task.Call(interruption, null);
await task.PollAsync(token);
await botData.FlushAsync(token);
}
What is the point of calling dialog.Void?
How can I use a ResumeAfter? If i add a ResumeAfter handler and await the result i get an error indicating it was expecting Call and got Poll
Is this code supposed to block until the dialog is complete? Because it does not
How can I push a proactive dialog and await its result?

Related

Bot Framework Proactive Message Passing Id Into Conversation State

I have a SMS / Twilio Channel that I'm using to send out a Proactive message to the user. To send the Proactive message I'm calling a API method passing in MySpecialId which is used later in the conversation.
I want to save this MySpecialId into the conversation but at the point I have the MySpecialId the conversation doesn't exist yet, and I don't have a turnContext, so I can't really save it yet.
Is there a way to pass this Id from my API method into the BotCallback? I created a quick example. (Here is the original example I'm using https://github.com/microsoft/BotBuilder-Samples/tree/master/samples/csharp_dotnetcore/16.proactive-messages)
Thanks
[HttpGet("{number}")]
public async Task<IActionResult> Get(string MySpecialId)
{
//For Twillio Channel
MicrosoftAppCredentials.TrustServiceUrl("https://sms.botframework.com/");
var NewConversation = new ConversationReference
{
User = new ChannelAccount { Id = $"+1{PHONENUMBERTOTEXTHERE}" },
Bot = new ChannelAccount { Id = "+1MYPHONENUMBERHERE" },
Conversation = new ConversationAccount { Id = $"+1{PHONENUMBERTOTEXTHERE}" },
ChannelId = "sms",
ServiceUrl = "https://sms.botframework.com/"
};
BotAdapter ba = (BotAdapter)_HttpAdapter;
await ((BotAdapter)_HttpAdapter).ContinueConversationAsync(_AppId, NewConversation, BotCallback, default(CancellationToken));
return new ContentResult()
{
Content = "<html><body><h1>Proactive messages have been sent.</h1></body></html>",
ContentType = "text/html",
StatusCode = (int)HttpStatusCode.OK,
};
}
private async Task BotCallback(ITurnContext turnContext, CancellationToken cancellationToken)
{
try
{
var MyConversationState = _ConversationState.CreateProperty<MyConversationData>(nameof(MyConversationData));
var ConversationState = await MyConversationState.GetAsync(turnContext, () => new MyConversationData(), cancellationToken);
//********************************************************************************************************************
ConversationState.MySpecialId = //HOW DO I GET MySpecialId FROM THE GET METHOD ABOVE HERE?
//********************************************************************************************************************
await _ConversationState.SaveChangesAsync((turnContext, false, cancellationToken);
await turnContext.SendActivityAsync("Starting proactive message bot call back");
}
catch (Exception ex)
{
this._Logger.LogError(ex.Message);
}
}
I don't believe that you can. Normally, you would do something like this by passing values through Activity.ChannelData, but ChannelData doesn't exist on ConversationReference.
Per comments below, #Ryan pointed out that you can pass data on ConversationAccount.Properties. Note, however, this is currently only available in the C# SDK. I've opened an issue to bring this into the Node SDK, but ETA is unknown at this point.
Instead, I'd suggest using a something more native to C#, like:
Create a ConcurrentDictionary
private ConcurrentDictionary<string, string> _idMap;
Map MySpecialId to Conversation.Id (in your Get function)
_idMap.AddOrUpdate(conversationReference.Conversation.Id, MySpecialId, (key, newValue) => MySpecialId);
Access the MySpecialId from the Activity.Conversation.Id (in BotCallback)
var ConversationState = await MyConversationState.GetAsync(turnContext, () => new MyConversationData(), cancellationToken);
ConversationState.MySpecialId = _idMap.GetValueOrDefault(turnContext.Activity.Conversation.Id);
Save ConversationState
await ConversationState.SaveChangesAsync(turnContext, false, cancellationToken);
There's other ways you could do this and some validation checks you'll need to add, but this should get you started.

Modify the destination for the response

I use Microsoft Bot Framework. From: https://dev.botframework.com/
AND
Microsoft Bot Emulator (V4 Preview) version 4.0.15-alpha. From: https://github.com/microsoft/botframework-emulator
I created a new C# project with "Bot Application" template. I run this project. I launched two entities of the Emulator.
Now I receive the message from the first Emulator entity but I want to send the response to the second Emulator entity. How can I do this?
This is the function where I try to modify the destination (the code that is commented) but does not work.
/// <summary>
/// POST: api/Messages
/// Receive a message from a user and reply to it
/// </summary>
public async Task<HttpResponseMessage> Post([FromBody]Activity activity)
{
//activity.Recipient.Id = "default-user";
//activity.ServiceUrl = "http://localhost:52234";
//activity.Conversation.Id = "e7bbb310-a93c-11e8-8dcc-7d6fd69e3901|livechat";
//activity.ReplyToId = "6cc291f0-a93d-11e8-9634-9f01a6c082d4";
if (activity.Type == ActivityTypes.Message)
{
await Conversation.SendAsync(activity, () => new Dialogs.RootDialog());
}
else
{
HandleSystemMessage(activity);
}
var response = Request.CreateResponse(HttpStatusCode.OK);
return response;
}
I launched two entities of the Emulator. Now I receive the message from the first Emulator entity but I want to send the response to the second Emulator entity. How can I do this?
Before you write code to send message to another emulator, you need to get the value of ServiceUrl and conversationId etc. And then you could refer to the following code to send message to the specified conversation.
In dialog:
await context.PostAsync($"{this.count++}: You said {activity.Text}");
var userAccount = new ChannelAccount(name: "User", id: "default-user");
var botAccount = new ChannelAccount(name: "Bot", id: "default-bot");
var connector = new ConnectorClient(new Uri("{your_ServiceUrl_here}"));
IMessageActivity message = Activity.CreateMessageActivity();
message.From = botAccount;
message.Recipient = userAccount;
//specify conversationId
message.Conversation = new ConversationAccount(id: "{your_conversationId_here}");
message.Text = $"You said {activity.Text} from emulator1";
message.Locale = "en-Us";
await connector.Conversations.SendToConversationAsync((Activity)message);
context.Wait(MessageReceivedAsync);
Test result:

How to integrate FormFlow and QnA dialogs in a single bot

How to integrate FormFlow and QnA dialogs in a simple bot. I'm not able to call FormFlow context once QnA is completed. If there are any samples for the same, then please share.
If you want to use QnA and FormFlow, create a dialog QnADialog and you can send all your messages first to the root dialog from there you can call your QnA Dialog like
var qnadialog = new QnADialog();
var messageToForward = await message;
await context.Forward(qnadialog, ResumeAfterQnA, messageToForward, CancellationToken.None);
Once th QnADilalog is executed, it will call the ResumeAfterQnA and there you can call your FormFlow Dialog.
private async Task ResumeAfterQnA(IDialogContext context, IAwaitable<object> results)
{
SampleForm form = new SampleForm();
var sampleForm = new FormDialog<SampleForm>(form, SampleForm.BuildForm, FormOptions.PromptInStart);
context.Call(sampleForm, RootDialog.SampleFormSubmitted);
}
You need to have a SampleFormSubmitted method that will be called after you form is submitted.
private async Task SampleFormSubmitted(IDialogContext context, IAwaitable<SampleForm> result)
{
try
{
var query = await result;
context.Done(true);
}
catch (FormCanceledException<SampleForm> e)
{
string reply;
if (e.InnerException == null)
{
reply = $"You quit. Maybe you can fill some other time.";
}
else
{
reply = $"Something went wrong. Please try again.";
}
context.Done(true);
await context.PostAsync(reply);
}
}
One approach is to start of from a Luis template.
Then make a specific Intent to start the Form.
Then you can have an empty Luis Intent of ”” and even ”None” and you put your QnA there.
That way the Qna will be on the background LUIS will give you great flexibility to trigger a specific dialogue with intents
Here is an example
http://www.garypretty.co.uk/2017/03/26/forwarding-activities-messages-to-other-dialogs-in-microsoft-bot-framework/

Typing indicator for bot framework in C#

I have a bot in Bot framework. Once user responds to the bot and bot is processing it, I want to show the typing indicator to the user in the meanwhile.
It is possible in Nodejs here - https://learn.microsoft.com/en-us/bot-framework/nodejs/bot-builder-nodejs-send-typing-indicator
But how can I implement it in C#?
You have to send an activity of type Typing.
You can do it like that:
// Send "typing" information
Activity reply = activity.CreateReply();
reply.Type = ActivityTypes.Typing;
reply.Text = null;
ConnectorClient connector = new ConnectorClient(new Uri(activity.ServiceUrl));
await connector.Conversations.ReplyToActivityAsync(reply);
You can also create the message using context.MakeMessage if you need to instantly respond in MessageReceivedAsync of a dialog.
var typingMsg = context.MakeMessage();
typingMsg.Type = ActivityTypes.Typing;
typingMsg.Text = null;
await context.PostAsync(typingMsg);
The typing indicator is replaced by the next message sent (at least in the Bot Framework emulator). This doesn't seem to work in Slack.
Here's another approach to send a typing indicator with a bot framework in C#, JavaScript, or Python.
Try use this:
var reply = activity.CreateReply();
reply.Text = null;
reply.Type = ActivityTypes.Typing;
await context.PostAsync(reply);
For showing typing indicator while bot is processing just add in OnTurnAsync function before await base.OnTurnAsync(turnContext, cancellationToken); :
await turnContext.SendActivityAsync(new Activity { Type = ActivityTypes.Typing }, cancellationToken);
If V4, following lines of code can help. Keep this code at such a place so that it gets executed everytime during the dialogue flow.
IMessageActivity reply1 = innerDc.Context.Activity.CreateReply();
reply1.Type = ActivityTypes.Typing;
reply1.Text = null;
await innerDc.Context.SendActivityAsync( reply1, cancellationToken: cancellationToken);
await Task.Delay(1000);

How do I email from BotFramework from Intent

I am looking for an example of how to send an email from a botframework intent.
I have tried the code below but nothing gets sent. I am doing something wrong?
[LuisIntent("TestEmailIntent")]
public async Task FindFundFactSheetAsync(IDialogContext context, LuisResult result)
{
var emailMessage = context.MakeMessage();
emailMessage.Recipient.Id = "myEmail#hotmail.com";
emailMessage.Recipient.Name = "John Cleophas";
emailMessage.Text ="Test message"
var data = new EmailContentData();
var channelData = Newtonsoft.Json.JsonConvert.SerializeObject(data);
emailMessage.ChannelData = channelData;
await context.PostAsync(emailMessage);
context.Wait(MessageReceived);
}
Unless your bot is employing the email channel, you will need to send the email message using your own code, not via the BotFramework. Any posts will go back to the original channel (i.e. Skype, Facebook, etc.)

Resources