Is it possible to include HeroCard attachment in WaterfallStepContext, i am able to call ITurnContext but not from WaterfallStepContext
Trying using C# code however .Activity is part of waterfallstep contenxt
var getFeedback = turnContext.Activity.CreateReply();
var feedbackChoices = new HeroCard
{
Text = "Our conversation was helpful?",
Buttons = new List<CardAction>
{
new CardAction() { Title = Constants.userResponseThumbsUp, Type = ActionTypes.ImBack, Value = Constants.userResponseYes},
new CardAction() { Title = Constants.userResponseThumbsDown, Type = ActionTypes.ImBack, Value = Constants.userResponseNo},
},
};
// Add the card to our reply to user.
getFeedback.Attachments = new List<Attachment>() { feedbackChoices.ToAttachment() };
await turnContext.SendActivityAsync(getFeedback, cancellationToken);
Yes, it is possible to include a HeroCard attachment in WaterfallStepContext. Here is the implementation below:
private static async Task<DialogTurnResult> ShowCardStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
var getFeedback = stepContext.Context.Activity.CreateReply();
var feedbackChoices = new HeroCard
{
Text = "Our conversation was helpful?",
Buttons = new List<CardAction>
{
new CardAction() { Title = Constants.userResponseThumbsUp, Type = ActionTypes.ImBack, Value = Constants.userResponseYes},
new CardAction() { Title = Constants.userResponseThumbsDown, Type = ActionTypes.ImBack, Value = Constants.userResponseNo},
},
};
// Add the card to our reply to user.
getFeedback.Attachments = new List<Attachment>() { feedbackChoices.ToAttachment() };
await stepContext.Context.SendActivityAsync(getFeedback, cancellationToken);
}
Hope this helps.
Related
I want to use facebook's quick replies -
at first I tried to use IMessageActivity's SuggestedActions property
IMessageActivity result = Activity.CreateMessageActivity();
result.Type = ActivityTypes.Message;
result.TextFormat = TextFormatTypes.Plain;
result.SuggestedActions = suggestedActions.Select(x => new CardAction()
{
Title = x,
Type = ActionTypes.ImBack,
Value = x
}).ToList()
but, as it didn't work for facebook, I found another solution based on ChanelData
result.ChannelData = JObject.FromObject(new {
quick_replies = suggestedActions.Actions.Select(x => new
{
content_type = "text",
title = x.Title,
payload = x.Value is null ? x.Title : x.Value
}).ToArray()
});
sadly it still doesn't work. Is there anything else to do?
I beleive you can use HeroCards and attach them to an activity and send that via SendActivityAsync.
Activity activity = _context.Context.Activity.CreateReply();
var buttonList = new List<CardAction>();
foreach (var prompt in Prompts)
{
buttonList.Add(
new CardAction { Value = prompt.DisplayText, Type = "imBack", Title = prompt.DisplayText, });
}
var heroCard = new HeroCard { Text = "message text", Subtitle = string.Empty, Buttons = buttonList };
activity.Attachments.Add(heroCard.ToAttachment());
await stepContext.Context.SendActivityAsync(activity).ConfigureAwait(false);
Try using a TextPrompt with the PromptOptions like this:
await stepContext.PromptAsync(nameof(TextPrompt), new PromptOptions
{
Prompt = new Activity
{
Type = ActivityTypes.Message,
Text = "¡Bien! Ahora te envié un código de acceso a tu teléfono. Por favor ingrésalo aquí cuando lo recibas.",
SuggestedActions = new SuggestedActions()
{
Actions = new List<CardAction>()
{
new CardAction() { Title = "Reenviar token", Type = ActionTypes.ImBack, Value = "Reenviar token" }
},
},
}
});
Result:
Edit:
If you dont want to use prompts, try this:
Activity activity = stepContext.Context.Activity.CreateReply();
activity.Type = ActivityTypes.Message;
activity.Text = "This is an example of SuggestedAction";
activity.SuggestedActions = new SuggestedActions()
{
Actions = new List<CardAction>()
{
new CardAction() { Title = "Action", Type = ActionTypes.ImBack, Value = "Action" }
}
};
await stepContext.Context.SendActivityAsync(activity);
return await stepContext.EndDialogAsync();
Result:
I have created one dialog to evaluate our company consultant in our chatbot (C# botframework), but I can't use for or foreach inside of AdaptiveCard. I need to create one TextBlock() and one ChoiceSet() block for each item from List Pergunta, but I can't do it with foreach. Any suggestion or better idea to create this loop ?
Here is my complete dialog code in C#.
using System;
using System.Threading.Tasks;
using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Connector;
using System.Collections.Generic;
using AdaptiveCards;
using SimpleEchoBot.Formulário;
namespace SimpleEchoBot.Dialogs
{
[Serializable]
public class RatingDialog : IDialog<EntidadeCliente>
{
public Task StartAsync(IDialogContext context)
{
context.Wait(this.MessageReceivedAsync);
return Task.CompletedTask;
}
public async virtual Task MessageReceivedAsync(IDialogContext context, IAwaitable<IMessageActivity> result)
{
var replyMessage = context.MakeMessage();
Attachment attachment = null;
attachment = CriarAdapativecard();
replyMessage.Attachments = new List<Attachment> { attachment };
await context.PostAsync(replyMessage);
}
public Attachment CriarAdapativecard()
{
AdaptiveCard card = new AdaptiveCard()
{
Body = new List<CardElement>()
{
new Container()
{
Items = new List<CardElement>()
{
new ColumnSet()
{
Columns = new List<Column>()
{
new Column()
{
Size = ColumnSize.Auto,
Items = new List<CardElement>()
{
new Image()
{
Url = "D:/EvaWeb.png",
Size = ImageSize.Medium,
Style = ImageStyle.Person
}
}
},
new Column()
{
Size = ColumnSize.Auto,
Items = new List<CardElement>()
{
new TextBlock()
{
Text = "Olá, temos uma novidade!",
Weight = TextWeight.Bolder,
IsSubtle = true
},
new TextBlock()
{
Text = "Gostaria de iniciar a avaliação do consultor?",
Wrap = true
}
}
}
}
}
}
}
},
// Buttons
Actions = new List<ActionBase>()
{
new ShowCardAction()
{
Title = "Sim",
Speak = "<s>Sim</s>",
Card = AvaliarConsultor("")
},
new ShowCardAction()
{
Title = "Não",
Speak = "<s>Não</s>",
Card = PularAvaliacao()
},
}
};
Attachment attachment = new Attachment()
{
ContentType = AdaptiveCard.ContentType,
Content = card
};
return attachment;
}
private static AdaptiveCard PularAvaliacao()
{
//does nothing on No option from user
return new AdaptiveCard()
{ };
}
private static AdaptiveCard AvaliarConsultor(List<string> Pergunta)
{
var adaptiveCard = new AdaptiveCard()
{
Body = new List<CardElement>()
{
new TextBlock()
{
Text = "Avaliação de questionário 1!",
Weight = TextWeight.Bolder,
Size = TextSize.Large
},
new TextBlock() { Text = Pergunta[0]},
new ChoiceSet()
{
Id = "nota",
Style = ChoiceInputStyle.Compact,
IsMultiSelect = false,
Choices = new List<Choice>()
{
new Choice()
{
Title = "⭐",
Value = "1"
},
new Choice()
{
Title = "⭐⭐",
Value = "2"
},
new Choice()
{
Title = "⭐⭐⭐",
Value = "3"
},
new Choice()
{
Title = "⭐⭐⭐⭐",
Value = "4"
},
new Choice()
{
Title = "⭐⭐⭐⭐⭐",
Value = "5"
}
}
}
},
Actions = new List<ActionBase>()
{
new SubmitAction()
{
Title = "Registrar",
Speak = "<s>Registrar avaliação</s>",
DataJson = "{ \"Type\": \"Registrar\" }"
}
}
};
return adaptiveCard;
}
}
}
I need to create one TextBlock() and one ChoiceSet() block for each item from List Pergunta, but I can't do it with foreach.
To achieve your requirement, please refer to the following code snippet.
private static AdaptiveCard AvaliarConsultor(List<string> Pergunta)
{
var adaptiveCard = new AdaptiveCard()
{
Body = new List<CardElement>()
{
new TextBlock()
{
Text = "Avaliação de questionário 1!",
Weight = TextWeight.Bolder,
Size = TextSize.Large
},
},
Actions = new List<ActionBase>()
{
new SubmitAction()
{
Title = "Registrar",
Speak = "<s>Registrar avaliação</s>",
DataJson = "{ \"Type\": \"Registrar\" }"
}
}
};
//dynamically generate TextBlock and ChoiceSet
//and then add to AdaptiveCard Body
foreach (var item in Pergunta)
{
var textblock = new TextBlock() { Text = item };
var choiceset = new ChoiceSet()
{
Id = "nota",
Style = ChoiceInputStyle.Compact,
IsMultiSelect = false,
Choices = new List<Choice>()
{
new Choice()
{
Title = "⭐",
Value = "1"
},
new Choice()
{
Title = "⭐⭐",
Value = "2"
},
new Choice()
{
Title = "⭐⭐⭐",
Value = "3"
},
new Choice()
{
Title = "⭐⭐⭐⭐",
Value = "4"
},
new Choice()
{
Title = "⭐⭐⭐⭐⭐",
Value = "5"
}
}
};
adaptiveCard.Body.Add(textblock);
adaptiveCard.Body.Add(choiceset);
}
return adaptiveCard;
}
Test result:
Bot generate multiple questions using backdoor events from webchat in sequence
without getting answer from user. Below example for two questions :
//Question 1
var reply = ((Activity)activity).CreateReply("Question 1");
reply.Type = ActivityTypes.Message;
reply.TextFormat = TextFormatTypes.Plain;
reply.SuggestedActions = new SuggestedActions()
{
Actions = new List<CardAction>()
{
new CardAction() {Title = "Ans 1", Type = ActionTypes.ImBack, Value = "Ans 1"},
new CardAction() {Title = "Ans 2", Type = ActionTypes.ImBack, Value = "Ans 2"}
}
};
await connectorClient.Conversations.ReplyToActivityAsync(reply);
//Question 2 after 10 sec
var reply = ((Activity)activity).CreateReply("Question 2");
reply.Type = ActivityTypes.Message;
reply.TextFormat = TextFormatTypes.Plain;
reply.SuggestedActions = new SuggestedActions()
{
Actions = new List<CardAction>()
{
new CardAction() {Title = "Ans 1", Type = ActionTypes.ImBack, Value = "Ans 1"},
new CardAction() {Title = "Ans 2", Type = ActionTypes.ImBack, Value = "Ans 2"}
}
};
await connectorClient.Conversations.ReplyToActivityAsync(reply);
How to recognize in receiving Activity activity on what question user answered- on Question 1 or Question 2?
The solution to this is very simple, all you need to do Is make the Value parameter unique. In code, it would look like the snippet below. You may also want to consider using ActionTypes.PostBack rather than ActionTypes.ImBack that way the user does not actually see the Value
var connectorClient = new ConnectorClient(new Uri(activity.ServiceUrl));
var reply = ((Activity)activity).CreateReply("Question 1");
reply.Type = ActivityTypes.Message;
reply.TextFormat = TextFormatTypes.Plain;
reply.SuggestedActions = new SuggestedActions()
{
Actions = new List<CardAction>()
{
new CardAction() {Title = "Ans 1", Type = ActionTypes.PostBack, Value = "question 1 Ans 1"},
new CardAction() {Title = "Ans 2", Type = ActionTypes.PostBack, Value = "question 1 Ans 2"}
}
};
await connectorClient.Conversations.ReplyToActivityAsync(reply);
//Question 2 after 10 sec
reply = ((Activity)activity).CreateReply("Question 2");
reply.Type = ActivityTypes.Message;
reply.TextFormat = TextFormatTypes.Plain;
reply.SuggestedActions = new SuggestedActions()
{
Actions = new List<CardAction>()
{
new CardAction() {Title = "Ans 1", Type = ActionTypes.PostBack, Value = "question 2 Ans 1"},
new CardAction() {Title = "Ans 2", Type = ActionTypes.PostBack, Value = "question 2 Ans 2"}
}
};
await connectorClient.Conversations.ReplyToActivityAsync(reply);
I am using Adaptive card combo box to show some categories of products. Once the user clicks on the category that category should pass to another method and display all the products of that category in another adaptive card Combo Box and let user select a product.
Here is the code to get all category to combo box.
public async Task GetCategoryAdaptiveCard(IDialogContext context)
{
var replyToConversation = context.MakeMessage();
replyToConversation.Attachments = new List<Attachment>();
HttpResponseMessage response = new HttpResponseMessage();
string query = string.Format(APIChatBot + "/AllCategory");
using (var client = ClientHelper.GetClient())
{
response = await client.GetAsync(query);
}
var categoryList = await response.Content.ReadAsAsync<IEnumerable<CategoryDTO>>();
AdaptiveCard adaptiveCard = new AdaptiveCard();
adaptiveCard.Speak = "Please Select A Category from the list";
adaptiveCard.Body.Add(new TextBlock()
{
Text = "Please Select A Category from the list",
Size = TextSize.Normal,
Weight = TextWeight.Normal
});
adaptiveCard.Body.Add(new TextBlock()
{
Text = "Category List",
Size = TextSize.Normal,
Weight = TextWeight.Normal
});
List<AdaptiveCards.Choice> list = new List<AdaptiveCards.Choice>();
foreach (var item in categoryList)
{
AdaptiveCards.Choice choice = new AdaptiveCards.Choice()
{
Title = item.CategoryName,
Value = item.Id.ToString()
};
list.Add(choice);
}
adaptiveCard.Body.Add(new ChoiceSet()
{
Id = "Category",
Style = ChoiceInputStyle.Compact,
Choices = list
});
Attachment attachment = new Attachment()
{
ContentType = AdaptiveCard.ContentType,
Content = adaptiveCard
};
replyToConversation.Attachments.Add(attachment);
await context.PostAsync(replyToConversation);
}
Here is the method i used to get the Product for the selected category.
public async Task GetProductForCategory(IDialogContext context, string category)
{
var replyToConversation = context.MakeMessage();
replyToConversation.Attachments = new List<Attachment>();
HttpResponseMessage response = new HttpResponseMessage();
string query = string.Format(APIChatBot + "/ProductByCategory/" + category);
using (var client = ClientHelper.GetClient())
{
response = await client.GetAsync(query);
}
var productList = await response.Content.ReadAsAsync<IEnumerable<ProductDTO>>();
if(productList .Count() == 0)
{
string message = "Sorry There Are No products For this Category" + category;
await context.PostAsync(message);
}
else
{
List<AdaptiveCards.Choice> list = new List<AdaptiveCards.Choice>();
foreach (var item in productList )
{
AdaptiveCards.Choice choice = new AdaptiveCards.Choice()
{
Title = item.ProductName,
Value = item.Id.ToString()
};
list.Add(choice);
}
AdaptiveCard adaptiveCard = new AdaptiveCard();
adaptiveCard.Body.Add(new TextBlock()
{
Text = "List of Products for the Category " + category,
Size = TextSize.Normal,
Weight = TextWeight.Normal
});
adaptiveCard.Body.Add(new TextBlock()
{
Text = "Please Select A Product From The List",
Size = TextSize.Normal,
Weight = TextWeight.Normal
});
adaptiveCard.Body.Add(new ChoiceSet()
{
Id = "ProductForCategory",
Style = ChoiceInputStyle.Compact,
Choices = list
});
Attachment attachment = new Attachment()
{
ContentType = AdaptiveCard.ContentType,
Content = adaptiveCard
};
replyToConversation.Attachments.Add(attachment);
await context.PostAsync(replyToConversation);
}
}
How can i pass the category selected by the user to the method that selects the product based on category?
If you create an adaptive card something like this:
var reply = context.MakeMessage();
var card = new AdaptiveCard();
var choices = new List<Choice>();
choices.Add(new Choice()
{
Title = "Category 1",
Value = "c1"
});
choices.Add(new Choice()
{
Title = "Category 2",
Value = "c2"
});
var choiceSet = new ChoiceSet()
{
IsMultiSelect = false,
Choices = choices,
Style = ChoiceInputStyle.Compact,
Id = "Category"
};
card.Body.Add(choiceSet);
card.Actions.Add(new SubmitAction() { Title = "Select Category", Data = Newtonsoft.Json.Linq.JObject.FromObject(new { button = "select" }) });
reply.Attachments.Add(new Attachment()
{
Content = card,
ContentType = AdaptiveCard.ContentType,
Name = $"Card"
});
await context.PostAsync(reply);
When the user selects one of the options, and clicks the button, the resulting activity's value will be a jobject you can deserialize and use to create a product specific adaptive card:
class CategorySelection
{
public string Category { get; set; }
}
var activityValue = activity.Value as Newtonsoft.Json.Linq.JObject;
if (activityValue != null)
{
var categorySelection = activityValue.ToObject<CategorySelection>();
var category = categorySelection.Category;
//query the database for products of this category type,
//create another adaptive card displaying the products and send to user
await context.PostAsync(reply);
}
I'm developing a bot with Microsoft Bot Framework, on Facebook Messenger the carousel shows as it should, but in Telegram it shows as 2 different cards on two different messages.
Doesn't Telegram support carousels or is it my fault?
Code:
public async Task Carousel(IDialogContext context, IAwaitable<IMessageActivity> activity)
{
var act = await activity;
//carousel
var replyToConversation = context.MakeMessage();
replyToConversation.Text = "2+ Cards are a Carousel";
replyToConversation.Recipient = message.From;
replyToConversation.Type = "message";
replyToConversation.Attachments = new List<Attachment>();
//1
List<CardImage> cardImages = new List<CardImage>();
cardImages.Add(new CardImage(url: "https://upload.wikimedia.org/wikipedia/commons/thumb/c/c0/Asimo_look_new_design.jpg/330px-Asimo_look_new_design.jpg"));
List<CardAction> cardButtons = new List<CardAction>();
CardAction plButton = new CardAction()
{
Value = "https://en.wikipedia.org/wiki/Robot",
Type = "openUrl",
Title = "WikiPedia Page"
};
cardButtons.Add(plButton);
HeroCard plCard = new HeroCard()
{
Title = "I'm a hero card",
Subtitle = "Robot Wikipedia Page",
Images = cardImages,
Buttons = cardButtons
};
Attachment plAttachment = plCard.ToAttachment();
replyToConversation.Attachments.Add(plAttachment);
//2
List<CardImage> cardImages2 = new List<CardImage>();
cardImages2.Add(new CardImage(url: "https://upload.wikimedia.org/wikipedia/en/thumb/9/9b/FANUC_6-axis_welding_robots.jpg/330px-FANUC_6-axis_welding_robots.jpg"));
List<CardAction> cardButtons2 = new List<CardAction>();
CardAction plButton2 = new CardAction()
{
Value = "https://en.wikipedia.org/wiki/Robot",
Type = "openUrl",
Title = "WikiPedia Page"
};
cardButtons2.Add(plButton);
HeroCard plCard2 = new HeroCard()
{
Title = "I'm a hero card",
Subtitle = "Robot Wikipedia Page",
Images = cardImages2,
Buttons = cardButtons2
};
Attachment plAttachment2 = plCard2.ToAttachment();
replyToConversation.AttachmentLayout = AttachmentLayoutTypes.Carousel;
replyToConversation.Attachments.Add(plAttachment2);
await context.PostAsync(replyToConversation);
}
It's not your fault. Per the Channel Inspector, the carousel in Telegram has a down-rendered appearance.