I want to create an adaptive card with buttons for email and call functionality. Whats the possible way for that? - botframework

I want to create an adaptive card with buttons for email and call functionality. I cannot find a format for call and email in adaptive cards. Is there a way to combine adaptive cards with hero cards, since hero cards have the cardAction object functionality ?

Adaptive Cards have action elements and selectactions that can be set. You would create a card with your input requirements and then use a submit action to process the input. Note on the text input elements, the format has been set to either email or tel
{"type":"AdaptiveCard","version":"1.0","id":"c2de1dd7-c916-4196-a914-0694957aff77","minVersion":"1.0","fallbackText":"","speak":"","body":[{"type":"TextBlock","id":"23af4d94-cf3b-480e-8c28-5a7988b8a26b","text":"Email Address","maxLines":1},{"type":"Input.Text","id":"4a47c737-dbf0-42d0-aa9f-10f4f38bd20f","placeholder":"enter your email here","value":"","style":"email","maxLength":250,"isRequired":false},{"type":"TextBlock","id":"b4f3bce9-2464-473f-9129-48a3740aec8b","size":"large","text":"OR","horizontalAlignment":"center","maxLines":1},{"type":"TextBlock","id":"fe2d84aa-79e7-4fdf-91a0-79a9eb264dc1","text":"Call me","maxLines":1},{"type":"Input.Text","id":"d03d538f-7ead-4959-8a8e-7703dbaf1899","placeholder":"What's your number?","value":"","style":"tel","maxLength":250,"isRequired":false}],"actions":[{"type":"Action.Submit","id":"8421a872-2c4f-4fa2-8254-b1d88503cc8a","data":"","title":"Email Me"},{"type":"Action.Submit","id":"2ccf2819-ad38-492a-80f9-7cd5152fea09","data":"","title":"Call Me"}]}

Is there a way to combine adaptive cards with hero cards, since hero cards have the cardAction object functionality ?
No, but Buttons within AdaptiveCard are not created using CardAction objects, you can use schema that is defined in AdaptiveCard instead.
There're three kinds of actions OpenUrl, Submit and ShowCard. Here Submit is what we need.
If you want to create AdaptiveCard in C#, you can for example create it and send the message like this:
AdaptiveCard card = new AdaptiveCard()
{
Body = new List<CardElement>()
{
new Container()
{
Speak = "<s>Hello!</s><s>Send Email!</s>",
Items = new List<CardElement>()
{
new TextBlock()
{
Text = "Hello!",
Weight = TextWeight.Bolder,
IsSubtle = true
},
new TextBlock()
{
Text = "Send Email!",
Wrap = true
},
new TextInput()
{
Id = "EmailAddTo",
Placeholder = "To:"
},
new TextInput()
{
Id = "EmailAddFrom",
Placeholder = "From:"
},
new TextInput()
{
Id = "Subject",
Placeholder = "Subject:"
},
new TextInput()
{
Id = "Content",
Placeholder = "Content:",
IsMultiline = true,
}
}
}
},
Actions = new List<ActionBase>()
{
new SubmitAction()
{
Title = "Send Email",
DataJson = "{\"Type\": \"EmailSend\"}"
}
}
};
Attachment attachment = new Attachment()
{
ContentType = AdaptiveCard.ContentType,
Content = card
};
var reply = context.MakeMessage();
reply.Attachments.Add(attachment);
await context.PostAsync(reply,CancellationToken.None);
When using Submit action, the Bot Framework will handle the submission and your bot will receive a new IMessageActivity with its Value, you can then handle it in your code for example like this:
private async Task MessageReceivedAsync(IDialogContext context, IAwaitable<IMessageActivity> result)
{
var message = await result;
if (message.Value != null)
{
// Got an Action Submit
dynamic value = message.Value;
string submitType = value.Type.ToString();
switch (submitType)
{
case "EmailSend":
/* */
return;
}
}
}
For more information, you can refer to the official Adaptive Cards Bot Sample.

Related

suggested actions inside waterfall step

I am using Bot Framework .Net SDK v4 and direct line web chat client. I have my dialogs as waterfall steps. Is it possible to use a suggested action inside a waterfall step? I am aware that a choice prompt will serve the purpose but I want the buttons to disappear after user clicks any choice, hence want to use suggested actions.
What I have tried.
In the constructor :
AddDialog(new ChoicePrompt(nameof(ChoicePrompt)));
In waterfall step:
public async Task<DialogTurnResult> FirstStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
var promptOptions = new PromptOptions()
{
Prompt = MessageFactory.Text("What card would you like to see? You can click or type the card name"),
RetryPrompt = MessageFactory.Text("That was not a valid choice, please select a card or number from 1 to 9."),
Choices = GetChoices(),
Style = ListStyle.SuggestedAction
};
return await stepContext.PromptAsync(nameof(ChoicePrompt), promptOptions);
}
private IList<Choice> GetChoices()
{
var cardOptions = new List<Choice>()
{
new Choice() { Value = "Card 1", Synonyms = new List<string>() { "adaptive" } },
new Choice() { Value = "Card 2", Synonyms = new List<string>() { "animation" } },
};
return cardOptions;
}
in the the prompt options you can specify you choices style :
var promptOptions = new PromptOptions {
Prompt = (Activity) MessageFactory.Attachment(card.ToAttachment()),
Style = ListStyle.SuggestedAction
};
return await stepContext.PromptAsync(nameof(ChoicePrompt), promptOptions, cancellationToken);
// Auto: Automatically select the appropriate style for the current channel.
// HeroCard: Add choices to prompt as a HeroCard with buttons.
// Inline : Add choices to prompt as an inline list.
// List : Add choices to prompt as a numbered list.
// None: Don't include any choices for prompt.
// SuggestedAction: Add choices to prompt as suggested actions.
source

I am using Bot framework V4.3, I want to retrieve adaptive card submit values

I'm using Bot framework V4.3, I have been using adaptive card in waterfall dialog, to get user information, I would want to get values once user clicks submit button and also I would like to go back to previous step if user click back button.
Here is how my adaptive card looks like
I have tried the solution given by #mdrichardson in Stack Overflow
But the adaptive card re-prompts again.
And the below code help us to go back to previous step but how to implement it to back button of adaptive card.
stepContext.ActiveDialog.State["stepIndex"] =(int)stepContext.ActiveDialog.State["stepIndex"] - 2;
Adding adaptive card to dialog. I had even used TextPrompt instead of ChoicePrompt
AddDialog(new ChoicePrompt("AdaptiveCardPrompt") { Style = ListStyle.None });
This is how I'm displaying adaptive card. My adaptive card is in Json format
cardAttachment = CreateAdaptiveCardAttachment();
return await stepContext.PromptAsync("AdaptiveCardPrompt",
new PromptOptions
{
Prompt = (Activity)MessageFactory.Attachment(new Attachment
{
ContentType = AdaptiveCard.ContentType,
Content = cardAttachment.Content
}),
}, cancellationToken);
Kindly help me in solving this issue. Thank you in advance
Edit from Botframework Support: Please do not use the code block below. It only works in Emulator. Instead, use:
if (string.IsNullOrWhiteSpace(activity.Text) && activity.Value != null)
{
activity.Text = JsonConvert.SerializeObject(activity.Value);
}
Edit 1: #mdrichardson Here is how I have setup the dialog call
public static async Task Run(this Dialog dialog, ITurnContext turnContext,IStatePropertyAccessor<DialogState> accessor, CancellationToken cancellationToken = default(CancellationToken))
{
var dialogSet = new DialogSet(accessor);
dialogSet.Add(dialog);
var dialogContext = await dialogSet.CreateContextAsync(turnContext, cancellationToken);
// Ensure that message is a postBack (like a submission from Adaptive Cards)
if (dialogContext.Context.Activity.GetType().GetProperty("ChannelData") != null)
{
var channelData = JObject.Parse(dialogContext.Context.Activity.ChannelData.ToString());
if (channelData.ContainsKey("postBack"))
{
var postbackActivity = dialogContext.Context.Activity;
// Convert the user's Adaptive Card input into the input of a Text Prompt
// Must be sent as a string
postbackActivity.Text = postbackActivity.Value.ToString();
await dialogContext.Context.SendActivityAsync(postbackActivity);
}
}
var results = await dialogContext.ContinueDialogAsync(cancellationToken);
if (results.Status == DialogTurnStatus.Empty)
{
await dialogContext.BeginDialogAsync(dialog.Id, null, cancellationToken);
}
}
And in OnTurnAsync method
if (turnContext.Activity.Type == ActivityTypes.Message)
{
await Dialog.Run(turnContext, ConversationState.CreateProperty<DialogState>(nameof(DialogState)), cancellationToken);
}
Edit 2 : I modified the code and I was able to go to next waterfall step. But I'm facing another issue here.
Next prompt is not getting displayed but I can see it in Log
This is how it shows in Emulator
Emulator View
Once user clicks the button control lands in MoreInfoAsync method
private async Task<DialogTurnResult> MoreInfoAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
var goback = JObject.Parse(stepContext.Result.ToString());
stepContext.Values["AdaptiveCardDetails"] = stepContext.Result.ToString();
if (goback.ContainsKey("goBack"))
{
return await stepContext.ReplaceDialogAsync(InitialDialogId);
}
// stepContext.ActiveDialog.State["stepIndex"] = (int)stepContext.ActiveDialog.State["stepIndex"] - 2;
else
return await stepContext.PromptAsync("MoreInfo", new PromptOptions { Prompt = MessageFactory.Text("Tell Me more.") }, cancellationToken);
}
I would like to go to initial dialog so I'm using ReplaceDialogAsync.
MoreInfo dialog is not displayed in emulator but its shown in log
Edit 3: Here is the complete code of waterfall steps
// This array defines how the Waterfall will execute.
var waterfallSteps = new WaterfallStep[]
{
ChoiceAsync,
CardAsync,
MoreInfoAsync,
ConfirmAsync
};
AddDialog(new WaterfallDialog(nameof(WaterfallDialog), waterfallSteps));
AddDialog(new ChoicePrompt("ChoiceType"));
AddDialog(new TextPrompt("AdaptiveCardPrompt"));
AddDialog(new TextPrompt("MoreInfo"));
InitialDialogId = nameof(WaterfallDialog);
private async Task<DialogTurnResult> ChoiceAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
options = new PromptOptions()
{
Prompt = MessageFactory.Text("Select the Choice"),
RetryPrompt = MessageFactory.Text("That was not a valid choice."),
Choices = GetChoices(),
Style = ListStyle.HeroCard
};
return await stepContext.PromptAsync("ChoiceType", options, cancellationToken);
}
private async Task<DialogTurnResult> CardAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
var cardAttachment = new Attachment();
stepContext.Values["leaveType"] = stepContext.Result.ToString();
cardAttachment = CreateAdaptiveCardAttachment();
return await stepContext.PromptAsync("AdaptiveCardPrompt",
new PromptOptions
{
Prompt = (Activity)MessageFactory.Attachment(new Attachment
{
ContentType = AdaptiveCard.ContentType,
Content = cardAttachment.Content,
}),
}, cancellationToken);
}
private async Task<DialogTurnResult> MoreInfoAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
var goback = JObject.Parse(stepContext.Result.ToString());
stepContext.Values["AdaptiveCardDetails"] = stepContext.Result.ToString();
if (goback.ContainsKey("goBack"))
{
return await stepContext.ReplaceDialogAsync(InitialDialogId);
}
else return await stepContext.PromptAsync("MoreInfo", new PromptOptions { Prompt = MessageFactory.Text("Tell Me more.") }, cancellationToken);
}
private async Task<DialogTurnResult> ConfirmAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
stepContext.Values["MoreInfo"] = stepContext.Result;
//As of now I wouldn't perform any task here so I'll end
return await stepContext.EndDialogAsync();
}
Dealing with the Re-Prompt
The issue is with your OnTurnAsync() method:
if (turnContext.Activity.Type == ActivityTypes.Message)
{
await Dialog.Run(turnContext, ConversationState.CreateProperty<DialogState>(nameof(DialogState)), cancellationToken);
}
Every time a user sends a message, it causes a new instance of your dialog to be run. Since Adaptive Card Input gets sent as a PostBack message (which is still a message), it causes the Dialog to run again, re-prompting the user.
If you're going to run dialogs from OnTurnAsync() or OnMessageAsync(), there's a couple of different things you should do, either:
Use if/switch statements. For example, if the message contains "help", run the HelpDialog, or
Start a dialog that saves user responses and skips steps as necessary. You can see an example of this in Core Bot's Booking Dialog. Notice how it's saving the user response in each step with something like bookingDetails.TravelDate = (string)stepContext.Result; and checks to see if it exists in the previous step before prompting with something like if (bookingDetails.TravelDate == null). For yours, you might store something like userProfile.AdaptiveCardDetails or something.
Back Button
To get the back button working, let's say it looks like this in your Adaptive Card:
{
"type": "Action.Submit",
"title": "Back",
"data": {
"goBack": "true",
}
},
When the user clicks "Back", the bot will receive an activity with:
Since the user wants to go back and you don't need the data, you could do something like:
var activity = turnContext.Activity;
if (string.IsNullOrWhiteSpace(activity.Text) && activity.Value.GetType().GetProperty("goBack"))
{
dc.Context.Activity.Text = "Back";
}
and then in your Dialog step:
if (stepContext.Result == "Back")
{
stepContext.ActiveDialog.State["stepIndex"] = (int)stepContext.ActiveDialog.State["stepIndex"] - 2;
}

Hero Card Display & HTML support for MSTeams

I am displaying a hero card which has several buttons. I would like to add a line break between the texts for each of the buttons. The channel that I am using is MSTeams
I have added HTML elements between the texts but that doesnt seem to help
List<CardAction> cardButtons = new List<CardAction>();
foreach (var item in items)
{
string CurrentNumber = Convert.ToString(item.number);
CardAction CardButton = new CardAction()
{
Type = "imBack",
Title = ""+CurrentNumber + "\n" + "Short Description: "+ item.short_description + "<ul><li>" + "Opened on: "+item.opened_at + "</li><li>" + "Incident state: "+myDict[item.incident_state]+ "</li></ul>"+"", //I tried this
Text = ""+CurrentNumber + "\n" + "Short Description: "+ item.short_description + "<ul><li>" + "Opened on: "+item.opened_at + "</li><li>" + "Incident state: "+myDict[item.incident_state]+ "</li></ul>"+"", //And this one too
Value = CurrentNumber
};
cardButtons.Add(CardButton);
}
HeroCard plCard = new HeroCard()
{
//Images = cardImages,
Buttons = cardButtons,
};
// Create an Attachment by calling the
// ToAttachment() method of the Hero Card
Attachment plAttachment = plCard.ToAttachment();
// Attach the Attachment to the reply
reply.Attachments.Add(plAttachment);
// set the AttachmentLayout as 'list'
reply.AttachmentLayout = "list";
While it works fine in the emulator if I add the "\n", in MS Teams, I dont get the same output.On hover, it appears that the text is appearing fine but not with MS Teams.
#Sash Sheen Teams supports formatting in cards only in the Text property. It is not supported for buttons.
Buttons are meant to be small and aren't supposed to have a lot of text on them. This is true across all channels.
My recommendation is to just include a short label on the button itself and have any additional required information elsewhere, like in the card's text. In your case, it seems like it would work out to just have the number on the button.

V4 Botframework ActionTypes.PostBack shows selected option in chat

We are building a bot where ActionType.Postback is not working.
There are places in the Bot where it does work however the example attached does not.
The sample was build using Bot framework 4.1.5.
Any help appreciated.
if (turnContext.Activity.Type == ActivityTypes.Message)
{
if (turnContext.Activity.Text == "help")
{
var reply = turnContext.Activity.CreateReply();
reply.Text = $"Hello {turnContext.Activity.From.Name}! How can i help you today? ";
var welcomeCard = new HeroCard
{
Buttons = new List<CardAction>
{
new CardAction {Title = "option1", Value = "option1", Type = ActionTypes.PostBack},
new CardAction {Title = "option2", Value = "option2", Type = ActionTypes.PostBack},
new CardAction {Title = "option3", Value = "option3", Type = ActionTypes.PostBack}
}
}.ToAttachment();
reply.Attachments.Add(welcomeCard);
await turnContext.SendActivityAsync(reply, cancellationToken);
}
else
{
This appears to just be an error in the Bot Framework Emulator and should work fine if you publish your bot to another channel. I've gone ahead and submitted this as a bug to the development team. https://github.com/Microsoft/BotFramework-Emulator/issues/1140
Note that the PostBack Action only works in certain channels and will default to ImShow if it is not supported. In the channels where PostBack is not supported, the response text value will be visible to all participants in the conversation.

how to get inputs from wearable devices

I'm implementing a notification system using Xamarin platform, which extends to wearable devices to send the notification. I also want to get the input of user from the wear notification and i have programed it in such away that user can select text or use voice. i followed the following tutorial
http://developer.android.com/training/wearables/notifications/voice-input.html
my code is:
void SendWearNotification (string message, string from)
{
var valuesForActivity = new Bundle();
valuesForActivity.PutString ("message", message);
String groupkey = "group_key_emails";
var intent = new Intent (this, typeof(MyMainActivity));
intent.PutExtras (valuesForActivity);
intent.AddFlags (ActivityFlags.ClearTop);
var pendingIntent = PendingIntent.GetActivity (this, 0, intent, PendingIntentFlags.OneShot);
var builder = new NotificationCompat.Builder (this)
.SetAutoCancel (true)
.SetContentIntent (pendingIntent)
.SetContentTitle (from)
.SetSmallIcon (Resource.Drawable.Iconlogo)
.SetContentText (message) //message is the one recieved from the notification
.SetTicker(from)
.SetGroup (groupkey) //creates groups
.SetPriority((int)NotificationPriority.High);
//
//for viewing the message in second page
var pagestyle= new NotificationCompat.BigTextStyle();
pagestyle.SetBigContentTitle (from)
.BigText (messagefromapp); //message from app is the one rerieved from the wcf app
//second page
var secondpagenotification = new NotificationCompat.Builder (this)
.SetStyle (pagestyle)
.Build ();
//intent for voice input or text selection
var wear_intent = new Intent (Intent.ActionView);
var wear_pending_intent = PendingIntent.GetActivity (this,0,wear_intent,0);
// Create the reply action and add the remote input
setRemoteInput ();
var action = new NotificationCompat.Action.Builder (Resource.Drawable.ic_mes,
GetString (Resource.String.messages), wear_pending_intent)
.AddRemoteInput (remoteinput)
.Build ();
//add it to the notification builder
Notification notification = builder.Extend (new NotificationCompat.WearableExtender ()
.AddPage (secondpagenotification).AddAction(action)).Build ();
//create different notitfication id so that we can as list
if(notification_id<9){
notification_id += 1;
}else{
notification_id=0;
}
var notificationManager = (NotificationManager)GetSystemService(Context.NotificationService);
notificationManager.Notify (notification_id+2, notification);
}
this method is implmented inside GCMListnerService class.
According to the tutorial from the above link, i can retreive the input data user selected or spoke uing the following code:
private void getResponse(Intent intent){
Bundle remoteInput = RemoteInput.GetResultsFromIntent(intent);
if (remoteInput != null) {
Toast.MakeText(this, remoteInput.GetCharSequence(EXTRA_VOICE_REPLY), ToastLength.Short);
}
//return null;
}
My question is when do i call this method, how do i know if user have selected a text en send from the wearable device. if there is any event which i can use.
I got the solution. the method the gets the remote input (the "getresponse" in my case) should be called from the "Oncreate" method of an activity that is used when the notification is created. In my case the actvity i used is "MyMainActivity" when i create the intent of the notification as u can see it in the code. So this means the method will be called twice, when the application runs, and when user reponds from the wear. but ony in the second case will the "remoteinput.getResultfromIntent" will have a value. I hope it will help for someone with same issues.

Resources