Invokeresponse in botframework v4 - botframework

How can I return an InvokeResponse in botframework v4 for C#? I need this to respond to compose extension activity messages. In the old framework this was done by returning in the response a composeExtension object from the controller.
How can this be done when implementing the IBot interface.
In the old framework there were MS Teams extensions, not available for the new framework version.

To respond to an invoke activity you have to set the "BotFrameworkAdapter.InvokeResponse" in turnContext.TurnState like in the below example
public async Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken = default(CancellationToken))
{
if (turnContext.Activity.Type == ActivityTypes.Message)
{
// do stuff
}
if (turnContext.Activity.Type == ActivityTypes.Invoke)
{
// do stuff
var invokeResponse = new InvokeResponse()
{
Body = response,
Status = (int)HttpStatusCode.OK
};
var activity = new Activity();
activity.Value = invokeResponse;
// set the response
turnCoontext.TurnState.Add<InvokeResponse>(InvokeReponseKey, activity);
}
}

For what I think you are asking:
There is an example of handling an invoke response in this sample. In your OnTurnAsync you need to catch the Invoke activity and do whatever it is you need to do with the activity like in the sample.
I'm unsure which SDK you are using as you did not include it in your question but, a simple example in C# (Node would be similar) might look like this:
public async Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken = default(CancellationToken))
{
if (turnContext.Activity.Type == ActivityTypes.Message)
{
//do stuff
}
if (turnContext.Activity.Type == ActivityTypes.Invoke)
{
//do stuff
}
}

From the BF SDK v4 code here:
https://github.com/Microsoft/botbuilder-dotnet/blob/4bb6b8d5faa4b252379ac331d6f5140ea27c177b/libraries/Microsoft.Bot.Builder/BotFrameworkAdapter.cs#L216
https://github.com/Microsoft/botbuilder-dotnet/blob/4bb6b8d5faa4b252379ac331d6f5140ea27c177b/libraries/Microsoft.Bot.Builder/BotFrameworkAdapter.cs#L285
what you do is use ITurnContext to "reply" with a fake activity of type ActivityTypesEx.InvokeResponse, setting Activity.Value to an InvokeResponse object with your desired status code and payload.

Related

Bot Framework v4 welcome message in Webchat Channel

I am working with Bot framework v4 webchat, trying to send a welcome message when a bot is initialized. First i send an event activity to the bot from webchat channel and got a 200 status response from but i can't able to capture the event activity in OneventActivityAsync action.
I have go though the below given solution.
https://github.com/microsoft/BotFramework-WebChat/tree/master/samples/04.api/a.welcome-event
OneventActivityAsync Section:
if (turnContext.Activity.Name == "webchat/join")
{
await turnContext.SendActivityAsync("Welcome Message!");
}
Could anyone help me on this?
I really appreciate any help :)
Your approach should work for v3. But for v4 you need to handle this event in OnMembersAddedAsync method.
protected override async Task OnMembersAddedAsync(IList<ChannelAccount> membersAdded, ITur
nContext<IConversationUpdateActivity> turnContext, CancellationToken cancellationToken)
{
if (turnContext.Activity.ChannelId != "webchat" && turnContext.Activity.ChannelId != "directline") {
foreach (var member in membersAdded)
{
if (member.Id != turnContext.Activity.Recipient.Id)
{
await turnContext.SendActivityAsync("Welcome");
}
}
}
}
Br,
Pdeepa

Kendo-ui JQuery not autostarting conversation with microsoft botframework

I want to use the Kendo-UI for JQuery as the front end for my bot.
This demo is what I'm trying to use. When you start the demo the bot automaticly greets you, but when I change the secret to mine it doesn't start the conversation. With my secret it just waits for user input before starting. When I test my bot with the bot framework emulator it does send a message when connecting.
I do not know why this happens. I did not change any code of the kendo demo, which makes me think that the problem lies with my bot framework. On the other hand the emulator does give me the desired outcome. I'm at a loss on how to solve my issue.
Below is the onMembersAddedAsync function of MyBot.cs, which to my understanding should send the greeting message when a new client connects.
protected override async Task OnMembersAddedAsync(IList<ChannelAccount> membersAdded, ITurnContext<IConversationUpdateActivity> turnContext, CancellationToken cancellationToken)
{
foreach (var member in membersAdded)
{
if (member.Id != turnContext.Activity.Recipient.Id)
{
//sends some messages to welcome the user + suggestedactions
await WelcomeNewUser(turnContext, cancellationToken);
}
}
}
I found a possible solution, instead of using the OnMembersAddedAsync function I send the welcome message from OnTurnAsync. I deleted the OnMembersAddedAsync because it doesn't do anything anymore.
public override async Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken = default)
{
await base.OnTurnAsync(turnContext, cancellationToken);
if (turnContext.Activity.Type == ActivityTypes.Message)
{
// Save any state changes that might have occured during the turn.
await ConversationState.SaveChangesAsync(turnContext, false, cancellationToken);
await UserState.SaveChangesAsync(turnContext, false, cancellationToken);
}
else
{
// Send an initial welcome message
if (turnContext.Activity.Type == ActivityTypes.ConversationUpdate && turnContext.Activity.MembersAdded.FirstOrDefault()?.Id == turnContext.Activity.Recipient.Id)
{
await WelcomeNewUser(turnContext, cancellationToken);
}
}
}

How do I return a Typing activity from my v4 Bot

I'm trying to add a Typing activity to a long-running action in my bot, but I keep getting a "BadGateway" error. Most of the samples I've found seem to be for bot framework v3, so the types or methods don't appear any more, and I've tried a few options for v4 (using C#), like the following:
await turnContext.SendActivityAsync(new Activity() { Type = ActivityTypes.Typing });
or
var typingActivity = new Activity()
{
Type = ActivityTypes.Typing
//RelatesTo = turnContext.Activity
};
typingActivity.ApplyConversationReference(typingActivity.GetConversationReference());
or
var act2 = MessageFactory.Text(null);
act2.Type = ActivityTypes.Typing;
await turnContext.SendActivityAsync(act2);
all of these result in a BadGateway error.
Can someone guide me on where I'm going wrong?
Your implementation is close, but needs a couple minor adjustments. Also, the text property is optional. If it's not needed, then you can simply remove it (same for the delay). This is what I use which adheres to the documentation (variable is used to match your code). You can reference the docs here.
var typingActivity = new Activity[] {
new Activity { Type = ActivityTypes.Typing },
new Activity { Type = "delay", Value= 3000 },
//MessageFactory.Text("Some message", "Some message"),
};
await turnContext.SendActivitiesAsync(typingActivity, cancellationToken);
Hope of help!
The answer of Steven Kanberg has the right code, but unfortunately this is a service issue at the moment, as confirmed in this issue on Github.
When the issue is resolved, it should be posted in the Github issue above.
Please try out this code to send a typing activity from your bot:
protected override async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
{
var typingActivity = MessageFactory.Text(string.Empty);
typingActivity.Type = ActivityTypes.Typing;
await turnContext.SendActivityAsync(typingActivity);
}

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.

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/

Resources