I’ve been learning NodeJS and have started to rebuild my Bot using v4 of the Bot Framework.
I have a Bot up and running in v3 using C# and it successfully uses a method where a QnA pairing is adapted into either a Hero Card, Video Card or standard reply depending on how many counts of a semi-colon the code finds.
I’m struggling to replicate this function in NodeJS using a string split method to determine what adaptive card should be used.
I’ve enclosed the original C# code and my partial attempt using NodeJS.
CSharp
protected override async Task RespondFromQnAMakerResultAsync(IDialogContext context, IMessageActivity message, QnAMakerResults result)
{
var answer = result.Answers.First().Answer;
Activity reply = ((Activity)context.Activity).CreateReply();
string[] qnaAnswerData = answer.Split(';');
int dataSize = qnaAnswerData.Length;
//image and video card
if (dataSize > 1 && dataSize <= 6)
{
var attachment = GetSelectedCard(answer);
reply.Attachments.Add(attachment);
await context.PostAsync(reply);
}
else
{
// await context.Forward(new BasicQnAMakerDialog(), AfterAnswerAsync, message, CancellationToken.None);
await context.PostAsync(answer);
}
}
private static Attachment GetSelectedCard(string answer)
{
int len = answer.Split(';').Length;
switch (len)
{
case 4: return GetHeroCard(answer);
case 6: return GetVideoCard(answer);
default: return GetHeroCard(answer);
}
}
private static Attachment GetHeroCard(string answer)
{
string[] qnaAnswerData = answer.Split(';');
string title = qnaAnswerData[0];
string description = qnaAnswerData[1];
string url = qnaAnswerData[2];
string imageURL = qnaAnswerData[3];
HeroCard card = null;
{
card = new HeroCard
{
Title = title,
Subtitle = description,
};
card.Buttons = new List<CardAction>
{
new CardAction(ActionTypes.OpenUrl, "Learn More", value: url)
};
card.Images = new List<CardImage>
{
new CardImage( url = imageURL)
};
}
return card.ToAttachment();
}
private static Attachment GetVideoCard(string answer)
{
string[] qnaAnswerData = answer.Split(';');
string title = qnaAnswerData[0];
string subtitle = qnaAnswerData[1];
string description = qnaAnswerData[2];
string thumbimageurl = qnaAnswerData[3];
string mediaUrl = qnaAnswerData[4];
string url = qnaAnswerData[5];
VideoCard card = new VideoCard
{
Title = title,
Subtitle = subtitle,
Text = description,
};
card.Image = new ThumbnailUrl
{
Url = thumbimageurl
};
card.Media = new List<MediaUrl>
{
new MediaUrl()
{
Url = mediaUrl
}
};
card.Buttons = new List<CardAction>
{
new CardAction()
{
Title = "View Full Screen",
Type = ActionTypes.OpenUrl,
Value = url
}
};
return card.ToAttachment();
}
Partial NodeJS
async onTurn(turnContext) {
if (turnContext.activity.type === ActivityTypes.Message) {
for (let i = 0; i < this.qnaServices.length; i++) {
// Perform a call to the QnA Maker service to retrieve matching Question and Answer pairs.
const qnaResults = await this.qnaServices[i].getAnswers(turnContext);
const qnaCard = qnaResults.includes(';');
// If an answer was received from QnA Maker, send the answer back to the user and exit.
if (qnaCard.toString().split(';').length > 3) {
await turnContext.sendActivity(qnaResults[0].answer);
await turnContext.sendActivity({
text: 'Video Card',
attachments: [CardFactory.adaptiveCard(VideoCard)]
});
} else if (qnaCard.toString().split(';').length < 3) {
await turnContext.sendActivity(qnaResults[0].answer);
await turnContext.sendActivity({
text: 'Hero Card',
attachments: [CardFactory.adaptiveCard(HeroCard)]
});
return;
}
}
The CardFactory in the v4 Node SDK has rendering functions for each type of card (HeroCards, VideoCards, AdaptiveCards, etc). You should use the corresponding function for the type of card you are trying to send. Your code should look like:
async onTurn(turnContext) {
if (turnContext.activity.type === ActivityTypes.Message) {
for (let i = 0; i < this.qnaServices.length; i++) {
// Perform a call to the QnA Maker service to retrieve matching Question and Answer pairs.
const qnaResults = await this.qnaServices[i].getAnswers(turnContext);
const qnaCard = qnaResults.includes(';');
// If an answer was received from QnA Maker, send the answer back to the user and exit.
if (qnaCard.toString().split(';').length > 3) {
await turnContext.sendActivity(qnaResults[0].answer);
await turnContext.sendActivity({
text: 'Video Card',
attachments: [CardFactory.videoCard(VideoCard)]
});
} else if (qnaCard.toString().split(';').length < 3) {
await turnContext.sendActivity(qnaResults[0].answer);
await turnContext.sendActivity({
text: 'Hero Card',
attachments: [CardFactory.heroCard(HeroCard)]
});
return;
}
}
}
}
Checkout the Using Cards and Using Adaptive Cards for more examples on how to send cards in the v4 BotFramework Node SDK.
Hope this helps!