Bot Builder v4: 'BadRequest' in SendActivityAsync() - botframework

I have a bot based in Echo bot (csharp_dotnetcore samples), running and reachable from Teams client.
I tried to send a simple Card (from Cards Bot) after the 'echo', but I get a 'BadRequest' error...
What can be wrong?
My code:
protected override async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
{
// OK
await turnContext.SendActivityAsync(MessageFactory.Text($"Echo1: {turnContext.Activity.Text}"), cancellationToken);
// 'BAD REQUEST'
var attachments = new List<Attachment>();
var message = MessageFactory.Attachment(attachments);
message.Attachments.Add(GetAnimationCard().ToAttachment());
await turnContext.SendActivityAsync(message, cancellationToken);
}
public static AnimationCard GetAnimationCard()
{
var animationCard = new AnimationCard
{
Title = "Microsoft Bot Framework",
Subtitle = "Animation Card",
Image = new ThumbnailUrl
{
Url = "https://learn.microsoft.com/en-us/bot-framework/media/how-it-works/architecture-resize.png",
},
Media = new List<MediaUrl>
{
new MediaUrl()
{
Url = "http://i.giphy.com/Ki55RUbOV5njy.gif",
},
},
};
return animationCard;
}
Thanks,
Diego

Animation
card
is not supported in Teams. Please take a look at Card
Reference
for supported cards in teams.

Related

How to open url in messaging extension?

I have updated my bot builder package from 4.6.0 to 4.9.0 i.e., the latest version.
We have handleTeamsMessagingExtensionFetchTask method which returns a promise. The return type is changed in version 4.9.0. The return type right now is the Promise of MessagingExtensionActionResponse.
With this return type, there is no way indicated in the documentation regarding opening a URL.
I have added the return type I was using in version 4.6.0 and that was working fine but it seems like there's no way in current update for performing such operations.
return await {
task: {
type: 'continue', value: {
width: 450,
height: 600,
title: 'Abc'
url: '********',
fallbackUrl: '*******'
}
}
} as MessagingExtensionActionResponse;
Initiate a message extension with open action URL. Please find the below piece of code
protected override async Task<MessagingExtensionActionResponse> OnTeamsMessagingExtensionFetchTaskAsync(ITurnContext<IInvokeActivity> turnContext, MessagingExtensionAction action, CancellationToken cancellationToken)
{
var response = new MessagingExtensionActionResponse()
{
Task = new TaskModuleContinueResponse()
{
Value = new TaskModuleTaskInfo()
{
Height = 720,
Width = 900,
Title = "Testing ME with URL,
Url = "https://1f0bd229.ngrok.io/myPage"
},
},
};
return response;
}
Let me know if this doesn't help you out

Welcome card doesnot pop up on its own while integrating it with direct line channel

I have created a bot locally using bot framework v4 c#. It has a welcome card that automatically pops up as soon I connect my local url with emulator, but recently I deployed my bot on azure and integrated it using direct line channel in my website. Now whenever I click, it opens the bot but the welcome card does not come on its own ,it appears when I write something from my chatbot. I just want the welcome card to appaer automatically as it appears in the emulator. Guys can you help me out please? Below is the code of direct line which I am integrating in my website.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<!-- Paste line 7 to 27 after the title tag in _Layout.cshtml -->
<link href="https://cdn.botframework.com/botframework-webchat/latest/botchat.css" rel="stylesheet"
/>
<script src="https://cdn.botframework.com/botframework-webchat/latest/botchat.js"></script>
<style>
#mychat {
margin: 10px;
position: fixed;
bottom: 30px;
left: 10px;
z-index: 1000000;
}
.botIcon {
float: left !important;
border-radius: 50%;
}
.userIcon {
float: right !important;
border-radius: 50%;
}
</style>
</head>
< body>
<!-- Paste line from 31 to 33 before the </body> tag at the end of code -->
<div id="container">
<img id="mychat" src=""/>
</div>
</body>
<!-- Paste line 38 to 88 after the </html> tag -->
<script>
(function () {
var div = document.createElement("div");
var user = {
id: "",
name: ''
};
var bot = {
id: '',
name: 'SaathiBot'
};
const botConnection = new BotChat.DirectLine({
secret: '',
webSocket: false
})
document.getElementsByTagName('body')[0].appendChild(div);
div.outerHTML = "<div id='botDiv' style='width: 400px; height: 0px; margin:10px; position:
fixed; bottom: 0; left:0; z-index: 1000;><div id='botTitleBar' style='height: 40px; width: 400px;
position:fixed; cursor: pointer;'>";
BotChat.App({
botConnection: botConnection,
user: user,
bot: bot
}, document.getElementById("botDiv"));
document.getElementsByClassName("wc-header")[0].setAttribute("id", "chatbotheader");
document.querySelector('body').addEventListener('click', function (e) {
e.target.matches = e.target.matches || e.target.msMatchesSelector;
if (e.target.matches('#chatbotheader')) {
var botDiv = document.querySelector('#botDiv');
botDiv.style.height = "0px";
document.getElementById("mychat").style.display = "block";
};
});
document.getElementById("mychat").addEventListener("click", function (e) {
document.getElementById("botDiv").style.height = '500px';
e.target.style.display = "none";
})
}());
</script>
Also here is my welcome card code in c#
namespace Microsoft.BotBuilderSamples
{
public class WelcomeUser : SaathiDialogBot<MainDialog>
{
protected readonly string[] _cards =
{
Path.Combine(".", "Resources", "WelcomeCard.json"),
};
public WelcomeUser(ConversationState conversationState, UserState userState, MainDialog dialog, ILogger<SaathiDialogBot<MainDialog>> logger)
: base(conversationState, userState, dialog, logger)
{
}
protected override async Task OnMembersAddedAsync(IList<ChannelAccount> membersAdded, ITurnContext<IConversationUpdateActivity> turnContext, CancellationToken cancellationToken)
{
await SendWelcomeMessageAsync(turnContext, cancellationToken);
Random r = new Random();
var cardAttachment = CreateAdaptiveCardAttachment(_cards[r.Next(_cards.Length)]);
await turnContext.SendActivityAsync(MessageFactory.Attachment(cardAttachment), cancellationToken);
}
private static async Task SendWelcomeMessageAsync(ITurnContext turnContext, CancellationToken cancellationToken)
{
foreach (var member in turnContext.Activity.MembersAdded)
{
if (member.Id != turnContext.Activity.Recipient.Id)
{
if (DateTime.Now.Hour < 12)
{
await turnContext.SendActivityAsync(
$"Hi,Good Morning {member.Name}",
cancellationToken: cancellationToken);
}
else if (DateTime.Now.Hour < 17)
{
await turnContext.SendActivityAsync(
$"Hi,Good Afternoon {member.Name}",
cancellationToken: cancellationToken);
}
else
{
await turnContext.SendActivityAsync(
$"Hi,Good Evening {member.Name}",
cancellationToken: cancellationToken);
}
}
}
}
private static Attachment CreateAdaptiveCardAttachment(string filePath)
{
var adaptiveCardJson = File.ReadAllText(filePath);
var adaptiveCardAttachment = new Attachment()
{
ContentType = "application/vnd.microsoft.card.adaptive",
Content = JsonConvert.DeserializeObject(adaptiveCardJson),
};
return adaptiveCardAttachment;
}
}
}
Here the saathiDialog code which inherited in welcome card. These are the two files inside my bot folder
public class SaathiDialogBot<T> : ActivityHandler where T : Dialog
{
protected readonly BotState ConversationState;
protected readonly Dialog Dialog;
protected readonly ILogger Logger;
protected readonly BotState UserState;
private DialogSet Dialogs { get; set; }
public SaathiDialogBot(ConversationState conversationState, UserState userState, T dialog, ILogger<SaathiDialogBot<T>> logger)
{
ConversationState = conversationState;
UserState = userState;
Dialog = dialog;
Logger = logger;
}
public override async Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken = default(CancellationToken))
{
var activity = turnContext.Activity;
if (string.IsNullOrWhiteSpace(activity.Text) && activity.Value != null)
{
activity.Text = JsonConvert.SerializeObject(activity.Value);
}
if (turnContext.Activity.Text == "Yes")
{
await turnContext.SendActivityAsync($"Good bye. I will be here if you need me. ", cancellationToken: cancellationToken);
await turnContext.SendActivityAsync($"Say Hi to wake me up.", cancellationToken: cancellationToken);
}
else
{
await base.OnTurnAsync(turnContext, cancellationToken);
}
await ConversationState.SaveChangesAsync(turnContext, false, cancellationToken);
await UserState.SaveChangesAsync(turnContext, false, cancellationToken);
}
protected override async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
{
Logger.LogInformation("Running dialog with Message Activity.");
await Dialog.RunAsync(turnContext, ConversationState.CreateProperty<DialogState>(nameof(DialogState)), cancellationToken);
}
}
}
here is main Dialog code
namespace Microsoft.BotBuilderSamples
{
public class MainDialog : ComponentDialog
{
private const string UserInfo = "value-userInfo";
protected readonly ILogger _logger;
protected readonly string[] _cards =
{
Path.Combine(".", "Resources", "ValidationCard.json"),
};
public MainDialog(ILogger<MainDialog> logger) : base(nameof(MainDialog))
{
_logger = logger;
AddDialog(new ProductIssue($"{nameof(MainDialog)}.fromMain"));
AddDialog(new ProductIssue($"{nameof(Confirm)}.fromConfirm"));
AddDialog(new ProductIssue($"{ nameof(Resolution)}.resolution"));
AddDialog(new Confirm());
AddDialog(new ChoicePrompt($"{nameof(MainDialog)}.issue"));
AddDialog(new TextPrompt($"{nameof(MainDialog)}.callDialog"));
AddDialog(new NumberPrompt<int>($"{nameof(MainDialog)}.num", valinatiotionAsync));
AddDialog(new WaterfallDialog($"{nameof(MainDialog)}.mainFlow", new WaterfallStep[]
{
MoblieNumberAsync,
ChoiceCardStepAsync,
ShowCardStepAsync,
CallingDialogsAsync
}));
AddDialog(new TextPrompt(nameof(TextPrompt)));
InitialDialogId = $"{nameof(MainDialog)}.mainFlow";
}
private async Task<DialogTurnResult> MoblieNumberAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
stepContext.Values[UserInfo] = new UserInput();
var options = new PromptOptions()
{
Prompt = MessageFactory.Text("Kindly enter your 10 digit mobile number without any spaces, dashes and country code. We will be sending an OTP later to this number "),
RetryPrompt = MessageFactory.Text("Incorrect mobile number entered. Please only enter the 10 digits of your mobile without any spaces, dashes and country code.")
};
return await stepContext.PromptAsync($"{nameof(MainDialog)}.num", options, cancellationToken);
}
private async Task<DialogTurnResult> ChoiceCardStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
BotAPIBLL botApiBLL = new BotAPIBLL();
var response = botApiBLL.GetCustomerDetail(stepContext.Context.Activity.Text);
await stepContext.Context.SendActivityAsync(MessageFactory.Text("Fetching your details from our systems. This may take a moment"), cancellationToken);
var options = new PromptOptions()
{
Prompt = MessageFactory.Text("Welcome user, How can we serve you ? "),
RetryPrompt = MessageFactory.Text("That was not a valid choice, please select a option between 1 to 4."),
Choices = GetChoices(),
};
return await stepContext.PromptAsync($"{nameof(MainDialog)}.issue", options, cancellationToken);
}
private async Task<DialogTurnResult> ShowCardStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
var attachments = new List<Attachment>();
var reply = MessageFactory.Text("");
var user_choice = ((FoundChoice)stepContext.Result).Value;
switch (user_choice)
{
case "Product issue":
reply.Attachments.Add(Cards.CreateAdaptiveCardAttachment3());
break;
case "Register Product":
reply.Attachments.Add(Cards.GetHeroCard1().ToAttachment());
break;
case "Online Purchase":
reply.Attachments.Add(Cards.GetHeroCard2().ToAttachment());
break;
case "Customer Grivance":
reply.Attachments.Add(Cards.GetHeroCard3().ToAttachment());
break;
default:
reply.AttachmentLayout = AttachmentLayoutTypes.Carousel;
reply.Attachments.Add(Cards.CreateAdaptiveCardAttachment3());
reply.AttachmentLayout = AttachmentLayoutTypes.Carousel;
reply.Attachments.Add(Cards.GetHeroCard1().ToAttachment());
break;
}
if (user_choice == "Register Product" || user_choice == "Online Purchase" || user_choice == "Customer Grivance")
{
await stepContext.Context.SendActivityAsync(reply, cancellationToken);
Random r = new Random();
var validationcard = Cards.CreateAdaptiveCardAttachment2(_cards[r.Next(_cards.Length)]);
await stepContext.Context.SendActivityAsync(MessageFactory.Attachment(validationcard), cancellationToken);
return await stepContext.EndDialogAsync(null, cancellationToken);
}
else
{
var options2 = new PromptOptions() { Prompt = reply, RetryPrompt = MessageFactory.Text("Retry") };
return await stepContext.PromptAsync($"{nameof(MainDialog)}.callDialog", options2, cancellationToken);
}
}
private async Task<DialogTurnResult> CallingDialogsAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
string choice = stepContext.Result.ToString();
if (choice.ToLower() == "no")
{
return await stepContext.BeginDialogAsync($"{nameof(MainDialog)}.fromMain", null, cancellationToken);
}
else
return await stepContext.BeginDialogAsync(nameof(Confirm), null, cancellationToken);
}
private IList<Choice> GetChoices()
{
var cardOptions = new List<Choice>()
{
new Choice() { Value = "Product issue", Synonyms = new List<string>() { "adaptive" } },
new Choice() { Value = "Register Product", Synonyms = new List<string>() { "hero" } },
new Choice() { Value = "Online Purchase", Synonyms = new List<string>() { "hero" } },
new Choice() { Value = "Customer Grivance", Synonyms = new List<string>() { "hero" } },
};
return cardOptions;
}
private Task<bool> valinatiotionAsync(PromptValidatorContext<int> promptContext, CancellationToken cancellationToken)
{
string value = (string)promptContext.Context.Activity.Text;
if (Regex.IsMatch(value, "^[0-9]{10}$"))
{
return Task.FromResult(true);
}
else
{
return Task.FromResult(false);
}
}
}
}
You have to use the ConversationUpdate event ActivityTypes.ConversationUpdate
This is an example inside of a Switch statement where i check the different ActivityTypes i get, and the conversationUpdate is used to show the welcome message
case ActivityTypes.ConversationUpdate:
{
if (activity.MembersAdded?.Count > 0)
{
await innerDc.BeginDialogAsync(nameof(dialog));
}
break;
}
UPDATE
Look at this working example and explanation on how to send a Welcome Message Event from WebChat.
If you are using WebChat or directline, the bot’s ConversationUpdate is sent when the conversation is created and the user sides’ ConversationUpdate is sent when they first send a message. When ConversationUpdate is initially sent, there isn’t enough information in the message to construct the dialog stack. The reason that this appears to work in the emulator, is that the emulator simulates a sort of pseudo DirectLine, but both conversationUpdates are resolved at the same time in the emulator, and this is not the case for how the actual service performs.
A workaround would be to send a back channel welcome event to the bot when the DirectLine connection is established and send a welcome message from the onEventAsync handler instead of onMembersAdded.
Embedded HTML for Web Chat
<!DOCTYPE html>
<html>
<head>
<script src="https://cdn.botframework.com/botframework-webchat/latest/webchat.js"></script>
<style>
#webchat {
height: 100%;
width: 100%;
}
</style>
</head>
<body>
<div style="display: flex">
<div style="position: relative; height: 500px; width: 500px"><div id="bot" ></div></div>
</div>
<script>
(async function() {
const res = await fetch('/directline/token', { method: 'POST' });
const { token } = await res.json();
var userinfo = {
id: 'user-id',
name: 'user name',
locale: 'es'
};
var botConnection = new window.BotChat.DirectLine({ token });
botConnection.connectionStatus$
.subscribe(connectionStatus => {
switch(connectionStatus) {
case window.BotChat.ConnectionStatus.Online:
botConnection.postActivity({
from: { id: 'myUserId', name: 'myUserName' },
type: 'event',
name: 'webchat/join',
value: { locale: 'en-US' }
}).subscribe(
id => console.log("Posted welcome event, assigned ID ", id),
error => console.log("Error posting activity", error)
);
break;
}
});
BotChat.App({
botConnection: botConnection,
user: userinfo,
bot: { id: 'botid' },
resize: 'detect'
}, document.getElementById("bot"));
})().catch(err => console.log(err));
</script>
</body>
</html>
Bot Code in C#
protected override async Task OnEventActivityAsync(ITurnContext<IEventActivity> turnContext, CancellationToken cancellationToken)
{
if (turnContext.Activity.Name == "webchat/join") {
await turnContext.SendActivityAsync("Welcome Message!");
}
}
protected override async Task OnMembersAddedAsync(IList<ChannelAccount> membersAdded, ITurnContext<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($"Hi there - {member.Name}. {WelcomeMessage}", cancellationToken: cancellationToken);
await turnContext.SendActivityAsync(InfoMessage, cancellationToken: cancellationToken);
await turnContext.SendActivityAsync(PatternMessage, cancellationToken: cancellationToken);
}
}
}
}
Hope this helps.

Adaptive card speech capabily in Web-chat

I have created a bot with an adaptive card with speak property set.
AdaptiveCard card = new AdaptiveCard()
{
Body = new List<CardElement>()
{
new Container()
{
Speak = "<s>Hello!</s><s>Are you looking for a flight or a hotel?</s>",
Items = new List<CardElement>()
{
new ColumnSet()
{
Columns = new List<Column>()
{
new Column()
{
Size = ColumnSize.Auto,
Items = new List<CardElement>()
{
new Image()
{
Url = "https://placeholdit.imgix.net/~text?txtsize=65&txt=Adaptive+Cards&w=300&h=300",
Size = ImageSize.Medium,
Style = ImageStyle.Person
}
}
},
new Column()
{
Size = ColumnSize.Stretch,
Items = new List<CardElement>()
{
new TextBlock()
{
Text = "Hello!",
Weight = TextWeight.Bolder,
IsSubtle = true
},
new TextBlock()
{
Text = "Are you looking for a flight or a hotel?",
Wrap = true
}
}
}
}
}
}
}
},
// Buttons
Actions = new List<ActionBase>() {
new ShowCardAction()
{
Title = "Hotels",
Speak = "<s>Hotels</s>",
Card = GetHotelSearchCard()
},
new ShowCardAction()
{
Title = "Flights",
Speak = "<s>Flights</s>",
Card = new AdaptiveCard()
{
Body = new List<CardElement>()
{
new TextBlock()
{
Text = "Flights is not implemented =(",
Speak = "<s>Flights is not implemented</s>",
Weight = TextWeight.Bolder
}
}
}
}
}
};
I am then using this bot from the web-chat control and using Microsoft Cognitive services. Here is the integration code:-
const speechOptions = {
speechRecognizer: new CognitiveServices.SpeechRecognizer({ subscriptionKey: 'YOUR_COGNITIVE_SPEECH_API_KEY' }),
speechSynthesizer: new CognitiveServices.SpeechSynthesizer({
gender: CognitiveServices.SynthesisGender.Female,
subscriptionKey: 'YOUR_COGNITIVE_SPEECH_API_KEY',
voiceName: 'Microsoft Server Speech Text to Speech Voice (en-US, JessaRUS)'
})
};
I am using my Cognitive Services API key here.
Q.1) I'm invoking the adaptive card through speech. My expectation is that when the card is delivered, it should read out the content within the SSML tags. But that isn't happening.The adaptive card is simply delivered without voice.
Q.2) For any text content that is delivered through context.PostAsync(), it is read aloud by the web-chat. But I was expecting it to be read aloud only if we use context.SayAsync().
Please help me understand this behavior and how I can deliver the adaptive card with speech. Thanks!

Why the resume function don't work for context.Forward in bot framework?

I'm new in Bot framework and I face this issue:
I want to move from dialog to another dialog, the callback function is working for context.Call but not for context.Forward?
I try multiple solutions such as put this.CallbackFunctionName but it dosen't work.
Here is my code for call new dialog:
switch (submitType)
{
case "alarm":
context.Call(new AlarmDialog(), ResumeAfterAlarmDialog);
context.Done(true);
return;
case "game":
await context.Forward(new AlarmDialog(), ResumeAfterAlarmDialog, value, CancellationToken.None);
return;
}
And here the method that I call:
private async Task ResumeAfterAlarmDialog(IDialogContext context, IAwaitable<object> result)
{
await context.PostAsync($"You are finish the alarm dialog");
context.Wait(this.MessageReceivedAsync);
}
And this is the error that I have for the context.Forward:
cannot use a method group as an argument to a dynamically dispatched operation.Did you intend to invoke the method?
Here the full implementation of the class:
namespace CardEx.Dialogs
{
[Serializable]
public class RootDialog : IDialog<object>
{
public Task StartAsync(IDialogContext context)
{
context.Wait(MessageReceivedAsync);
return Task.CompletedTask;
}
private async Task MessageReceivedAsync(IDialogContext context, IAwaitable<object> result)
{
var activity = await result as Activity;
if (activity.Value != null)
{
// Got an Action Submit
dynamic value = activity.Value;
string submitType = value.Type.ToString();
switch (submitType)
{
case "alarm":
context.Call(new AlarmDialog(), ResumeAfterAlarmDialog);
return;
case "alarm2":
await context.Forward(new AlarmDialog(), ResumeAfterAlarmDialog, value, CancellationToken.None);
return;
}
}
AdaptiveCard aCard = new AdaptiveCard()
{
Body = new List<AdaptiveElement>()
{
new AdaptiveTextBlock()
{
Text = "Welcome!",
Weight = AdaptiveTextWeight.Bolder,
Size = AdaptiveTextSize.Large
},
new AdaptiveTextBlock() { Text = "Please choose one of the following:" },
},
Actions = new List<AdaptiveAction>()
{
new AdaptiveSubmitAction()
{
Title = "Set an alarm",
DataJson = "{ \"Type\": \"alarm\" }"
},
new AdaptiveSubmitAction()
{
Title = "Play a game",
DataJson = "{ \"Type\": \"game\" }"
}
}
};
Attachment attachment = new Attachment()
{
ContentType = AdaptiveCard.ContentType,
Content = aCard
};
var reply = context.MakeMessage();
reply.Attachments.Add(attachment);
await context.PostAsync(reply, CancellationToken.None);
context.Wait(MessageReceivedAsync);
}
private async Task ResumeAfterAlarmDialog(IDialogContext context, IAwaitable<object> result)
{
await context.PostAsync($"You are finish the alarm dialog");
context.Wait(this.MessageReceivedAsync);
}
}
}
Thank you.
cannot use a method group as an argument to a dynamically dispatched operation.Did you intend to invoke the method?
the parameter value used in context.Forward is a dynamic type, which raised this issue.
Please try following code:
await context.Forward(new TestDialog(), ResumeAfterAlarmDialog, (object)value, CancellationToken.None);

ReplyToId' cannot be null

This exception is continuously throwing. This started since i updated botframework to 3.5.3.
Code :
MessagesController :
await Conversation.SendAsync(activity, () => new DefaultDialog());
Than in my DefaultDialog :
private async Task MessageReceivedAsync(IDialogContext context, IAwaitable<IMessageActivity> argument)
{
var msg = await argument;
await Helper.CallMenu(context, msg);
In CallMenu function :
internal static async Task CallMenu(IDialogContext context, IMessageActivity msg)
{
await MenuFirstPart(context, msg);
In MenuFirstPart function :
internal static async Task MenuFirstPart(IDialogContext context, IMessageActivity msg)
{
await context.PostAsync("I can assist you with : ");
msg.AttachmentLayout = AttachmentLayoutTypes.Carousel;
msg.Attachments = new List<Attachment>();
ThumbnailCard thumbnailCard = new ThumbnailCard()
{
Buttons = new List<CardAction>
{
new CardAction ()
{
Value = "Method_News",
Type = "postBack",
Title = "News"
},
new CardAction()
{
Value = "Method_About",
Type = "postBack",
Title = "About"
},
new CardAction ()
{
Value = "Method_Help",
Type = "postBack",
Title = "Help"
},
},
};
msg.Attachments.Add(thumbnailCard.ToAttachment());
await context.PostAsync(msg);
}
Exception is thrown when context is trying to post msg.
Any help ? Or how to downgrade my bot to 3.5.0. This worked fine there ...
You are using the incoming message (msg) and sending that back.
You need to create a new message instead. Use the following:
var reply = context.MakeMessage();

Resources