4

I'm working on a Bot with the Bot Framework v4 in C#. What I want now is, that after sending an Adaptive Card to the user, with Actions, that I can update this card after the Action is fired. For example, that the button will disappear after the click.

Link to NodeJS Solution

here I have a Solution in NodeJS, but I'm not able to transfer it to C#. Can anybody help?

More Project details: I have an Adaptive Card that looks like this: enter image description here

This Card will be generated, after I searched with the MS Graph from a SharePoint Library.

The Main Goal is, that i can use the Refiners to update the Card with a new Card from JSON. The "OK" button is a Submit Action which can be catched in the OnMessageActivityAsync Method. The Input Values are in the Activity Value so can create a Filter Method. my Problem is, that I can't update the Card that is already send to the User.

Before I sended the first Card with the Results to the User I write the Activity into a State, so I can Access OnMessageActivityAsync Method, but I'm not sure if this is the right approach.

protected override async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
    {
        Logger.LogInformation("Running dialog with Message Activity.");

        var txt = turnContext.Activity.Text;
        dynamic val = turnContext.Activity.Value;
        // Check if the activity came from a submit action
        if (string.IsNullOrEmpty(txt) && val != null)
        {
            await turnContext.SendActivityAsync(MessageFactory.Text($"Refiner Language: {val.id_language}"));
            await turnContext.SendActivityAsync(MessageFactory.Text($"Refiner MachType: {val.id_type}"));

            var r = new StreamReader("Cards/helloCard2.json");
            string json = r.ReadToEnd();
            AdaptiveCard card = AdaptiveCard.FromJson(json).Card;

            var docSearchState = await StateAccessor.GetAsync(turnContext);
            Activity activity = docSearchState.Activity;
            activity.Attachments = new List<Attachment>() {
                new Attachment()
                {
                    Name = "Card",
                    ContentType = AdaptiveCard.ContentType,
                    Content = card,
                }
            };

            await turnContext.UpdateActivityAsync(activity);
        }

This Code gives me the following Error Message:

fail: Microsoft.Bot.Builder.Integration.AspNet.Core.BotFrameworkHttpAdapter[0]
  Exception caught : Error reading JArray from JsonReader. Current JsonReader item is not an array: StartObject. Path 'DocumentSearchState.Activity.attachments.$values[0].content.body'.

Has anybody a good idea to solve this?

  • 2
    You should start work on this project and then ask for help when you reach a roadblock, instead of handing off the whole project. Show us what you have done so far. – mankowitz Sep 25 '19 at 14:56
  • Are you using the Teams channel? – Kyle Delaney Sep 26 '19 at 17:43
  • at the moment I'm using the bot emulator for testing. but at the end i want to use teams channel. this shouldn't be a channel specific problem. i think. – Thomas von Mentlen Sep 27 '19 at 15:26
  • You are trying to update an existing message in the conversation history. That is absolutely channel-specific functionality. Is my answer acceptable? – Kyle Delaney Sep 30 '19 at 20:31

2 Answers2

1

Have a look at this answer to get an idea of how to update Adaptive Cards.

You can either write code to remove the submit action from an Adaptive Card dynamically, or you can have two different versions of your card: one with the submit action and one without.

If you want this whole process to be made easier with prebuilt code that you can install in NuGet packages, feel free to voice your support for these ideas on GitHub:

Kyle Delaney
  • 11,616
  • 6
  • 39
  • 66
0

You can update adaptive card in channel like MS Teams not in directline. For updating card you need one Accessor to store ActivityId which is generated while sending current message.

var response = await stepContext.Context.SendActivityAsync(reply);
//Customize object to store ID
UserProfile user = new UserProfile(response.Id);
//Accessor to store card values to update it and disable buttons after click
await CardStateAccessor.SetAsync(stepContext.Context, user, cancellationToken);

After this in next waterfallstep you need to write logic to update this posted message.

UserProfile UserDataState = await CardStateAccessor.GetAsync(stepContext.Context, () => new UserProfile());
//Retrive accessor property
//Here I am updating adaptive card
var reply = stepContext.Context.Activity.CreateReply();
                string text = File.ReadAllText("./AdaptiveCards/UpdatedYesNo.json");
                var cardObj = JsonConvert.DeserializeObject(text);
                reply.Attachments = new List<Attachment>()
                {
                    new Attachment(){ContentType="application/vnd.microsoft.card.adaptive",Content=cardObj}
                };
                //Here you need to assign ID from accessor so that earlier posted msg gets updated
                reply.Id = UserDataState.ActivityId;
                //Use here UpdateActivityAsync to post updated message
                await stepContext.Context.UpdateActivityAsync(reply, cancellationToken);

Just addition info: For me goal was to disable buttons of adaptive card after click. So second time when I was posting adaptive card, I just removed all actions from buttons.

Amit Kp
  • 11
  • 2