Botframework-sdk: [Question] How can i get the response from card buttons?

Created on 7 May 2017  Â·  25Comments  Â·  Source: microsoft/botframework-sdk

I want to get the response of a hero card button click on the next waterfall?

How to do it?

I don't want to use dialogAction to route to another dialog. I have this cards which asks various questions and store the response in an object. I want to get the response to store.

Most helpful comment

When I´m using the postBack method on a HeroCard button, my script does not start the next step of the waterfall.
I´m looping the same step over and over again.
But the value of the button is in the session.message.text property.
I can parse this and redirect the manually, but that is not pretty handy.

function (session) {
    // Can get value of button here after hitting in one time and then manually redirect
    var msg = new builder.Message(session);
    msg.attachments([
        new builder.HeroCard(session)
            .title("Classic Gray T-Shirt")
            .buttons([
                builder.CardAction.postBack(session, "testData", "Buy")
            ])
    ]);
    session.send(msg);
}
function (session, results) {
    // Never reaching this step automatically when hitting a button
}

All 25 comments

@Nisthar are you using C# or Node SDK?

@nwhitmont I am using nodejs.

Offtopic, But i would like to ask this here: Does the session.dialogData holds the data from previous conversation. I mean does it automatically get cleared for each sessions? Do i have to manually session.dialogData.save() or session.dialogData.clear()?

You can find the response data in result.response.entity. For example:

var choicesArray = ['Hero Card', 'Slack Buttons', 'Basic text'];

bot.dialog('/', [
    function (session) {
        session.send('Hi, I am Slack Support Bot!\n\n I will show you what is possible with Bot Framework for Slack.');

        botBuilder.Prompts.choice(session, 'Choose a demo', choicesAray);
    },
    function (session, result) {
        console.log('Result object:\n'); // log the result object
        console.log(result);
/* will print:
Result object:

{ score: 1,
  resumed: 0,
  promptType: 3,
  response: { index: 0, entity: 'Hero Card', score: 1 },
  childId: 'BotBuilder:Prompts' }
*/
        switch (result.response.entity) {
            case 'Hero Card':
                session.beginDialog('herocard');
                break;
            case 'Slack Buttons':
                session.beginDialog('buttons');
                break;
            case 'Basic message':
                session.beginDialog('basicMessage');
                break;
            default:
                session.send('invalid choice');
                break;
        }
    }
]);

@nwhitmont

I was trying to get the response from herocard button click. Is it possible?

I can only see openUrl and some other predefined functions. I can build buttons with prompts. But is there any way to get the response from herocard button click?

@Nisthar I don't understand what you mean. What is the "response from herocard button click"?

Offtopic, But i would like to ask this here: Does the session.dialogData holds the data from previous conversation. I mean does it automatically get cleared for each sessions? Do i have to manually session.dialogData.save() or session.dialogData.clear()?

session.dialogData is a temporary variable that does not persist across conversations. No you do not have to call save or clear. If you want to persist data, then store it in session.userData or session.conversationData object. Read more about dialogs here.

ps. try to ask only one question per issue, thx

@nwhitmont Like how to know which button was clickied in hero card and call a function as a response for that button click?

@Nisthar can you share your code for your Hero Card and related dialog?

When you create a Hero Card using the botBuilder.HeroCard class, one of the properties is buttons which accepts an array of objects of the type botBuilder.CardAction. The available card actions are described on L75-105 (included below) - from https://github.com/Microsoft/BotBuilder/blob/master/Node/core/src/cards/CardAction.ts#L75

static call(session: Session, number: string, title?: string|string[]): CardAction {
        return new CardAction(session).type('call').value(number).title(title || "Click to call");
    }

    static openUrl(session: Session, url: string, title?: string|string[]): CardAction {
        return new CardAction(session).type('openUrl').value(url).title(title || "Click to open website in your browser");
    }

    static imBack(session: Session, msg: string, title?: string|string[]): CardAction {
        return new CardAction(session).type('imBack').value(msg).title(title || "Click to send response to bot");
    }

    static postBack(session: Session, msg: string, title?: string|string[]): CardAction {
        return new CardAction(session).type('postBack').value(msg).title(title || "Click to send response to bot");
    }

    static playAudio(session: Session, url: string, title?: string|string[]): CardAction {
        return new CardAction(session).type('playAudio').value(url).title(title || "Click to play audio file");
    }

    static playVideo(session: Session, url: string, title?: string|string[]): CardAction {
        return new CardAction(session).type('playVideo').value(url).title(title || "Click to play video");
    }

    static showImage(session: Session, url: string, title?: string|string[]): CardAction {
        return new CardAction(session).type('showImage').value(url).title(title || "Click to view image");
    }

    static downloadFile(session: Session, url: string, title?: string|string[]): CardAction {
        return new CardAction(session).type('downloadFile').value(url).title(title || "Click to download file");
    }

    static dialogAction(session: Session, action: string, data?: string, title?: string|string[]): CardAction {
        var value = 'action?' + action;
        if (data) {
            value += '=' + data;
        }
        return new CardAction(session).type('postBack').value(value).title(title || "Click to send response to bot");
    }

One way to approach this would be to send a postBack message to your bot, and then in the next waterfall step, parse the message from the card postBack action and use it to trigger your other function.

An example code would help me a lot. Because i can't actually understand
the function of postBack and how to use it.

On Tue, May 9, 2017 at 4:57 AM, Nils notifications@github.com wrote:

When you create a Hero Card using the botBuilder.HeroCard class, one of
the properties is buttons which accepts an array of objects of the type
botBuilder.CardAction. The available card actions are described on
L75-105 (included below) - from https://github.com/Microsoft/
BotBuilder/blob/master/Node/core/src/cards/CardAction.ts#L75

static call(session: Session, number: string, title?: string|string[]): CardAction {
return new CardAction(session).type('call').value(number).title(title || "Click to call");
}

static openUrl(session: Session, url: string, title?: string|string[]): CardAction {
    return new CardAction(session).type('openUrl').value(url).title(title || "Click to open website in your browser");
}

static imBack(session: Session, msg: string, title?: string|string[]): CardAction {
    return new CardAction(session).type('imBack').value(msg).title(title || "Click to send response to bot");
}

static postBack(session: Session, msg: string, title?: string|string[]): CardAction {
    return new CardAction(session).type('postBack').value(msg).title(title || "Click to send response to bot");
}

static playAudio(session: Session, url: string, title?: string|string[]): CardAction {
    return new CardAction(session).type('playAudio').value(url).title(title || "Click to play audio file");
}

static playVideo(session: Session, url: string, title?: string|string[]): CardAction {
    return new CardAction(session).type('playVideo').value(url).title(title || "Click to play video");
}

static showImage(session: Session, url: string, title?: string|string[]): CardAction {
    return new CardAction(session).type('showImage').value(url).title(title || "Click to view image");
}

static downloadFile(session: Session, url: string, title?: string|string[]): CardAction {
    return new CardAction(session).type('downloadFile').value(url).title(title || "Click to download file");
}

static dialogAction(session: Session, action: string, data?: string, title?: string|string[]): CardAction {
    var value = 'action?' + action;
    if (data) {
        value += '=' + data;
    }
    return new CardAction(session).type('postBack').value(value).title(title || "Click to send response to bot");
}

One way to approach this would be to send a postBack message to your bot,
and then in the next waterfall step, parse the message from the card
postBack action and use it to trigger your other function.

—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/Microsoft/BotBuilder/issues/2715#issuecomment-300018734,
or mute the thread
https://github.com/notifications/unsubscribe-auth/ALcELV-T_01b9ZCdzYev9Hzzdac0AK8Tks5r36TegaJpZM4NTDcv
.

When I´m using the postBack method on a HeroCard button, my script does not start the next step of the waterfall.
I´m looping the same step over and over again.
But the value of the button is in the session.message.text property.
I can parse this and redirect the manually, but that is not pretty handy.

function (session) {
    // Can get value of button here after hitting in one time and then manually redirect
    var msg = new builder.Message(session);
    msg.attachments([
        new builder.HeroCard(session)
            .title("Classic Gray T-Shirt")
            .buttons([
                builder.CardAction.postBack(session, "testData", "Buy")
            ])
    ]);
    session.send(msg);
}
function (session, results) {
    // Never reaching this step automatically when hitting a button
}

I think this is related to the question of @Nisthar

@schneiderpat I'm getting the exact same issue. Where clicking the (in your case Buy) button just triggers the same dialog stack again.

@pourmesomecode You are commenting on a closed issue. If you have a problem or question, please open a new issue. Thx

Did any one got resolution of this?
I am also facing the same problem. I can get the response if I use Prompts, but cannot get the response for the card inputs.

function (session, args, next) {
    var msg = new builder.Message(session);
    msg.attachments([
        new builder.HeroCard(session)
            .title(dbEntity.name)
            .buttons([
                builder.CardAction.postBack(session, dbEntity.action.data, dbEntity.action.title)
            ])
    ]);
    session.send(msg);
    // without next call the dialog stack starts over
    next();  // with next call the dialog stack will move to the next function without waiting for the user input
}
function (session, results) {
     // does not reach here, if next is not called in previous step
     // reaches here is next is called, but does not wait for user input, so results remains empty
     console.log(JSON.stringify(results));
}

@haligasd I think that link describes the wrong way of handling this scenario. In fact, that link is saying "Yeah, don't use HeroCards with buttons, because they don't work". What you basically need to do is handling the respose on the same step of the dialog. It is not very well documented and a weird way of doing it but I think that is best.

BTW: Thanks everyone on this thread for explaining the issue, as I was having the very same issue myself. This also happens with the Action.Submit in the AdaptiveCards buttons. You need to handle the response on the same step.

For now, the only solution is, the buttons on your hero card, should trigger another dialog. For example if your button has the text Send, you must have a dialog that should be triggered with a regex matches that except the word Send

A slightly more graceful way to do this might be to use the "dialogAction" functionality for a CardAction. You can use it in conjunction with a dialog's beginDialogAction action. That way you could pass additional data to whichever dialog you want to be triggered.

Here's a link to the Botbuilder NodeJS SDK referencing the same. I'm sure there must be something similar for C# too.

Sorry for commenting on a closed issue, but thought this could help people.

@nwhitmont sorry to comment on the closed issue. How will we send the object to the another dialog when someone clicks a imBack or dialogAction button. As far as I see everywhere it is taking only string.

Maybe you could use JSON.stringify and JSON.parse. I am doing that to send an object. @jagadeeshmarthy

Yes,@sfratini. I have found that, as of now it is the only way.

This works for me

  bot.dialog('example', [
    (session, args, next) => {
      const choices = ['foo', 'bar', 'baz']

      const cards = choices.map(item => {
        // return new builder.ThumbnailCard(session) // You can use thumbnails
        return new builder.HeroCard(session)
          .buttons([builder.CardAction.imBack(session, item, item)])
          .images([builder.CardImage.create(
            session, `http://via.placeholder.com/300x150?text=${item}`
          )])
      })

      const msg = new builder.Message(session)
        // .attachmentLayout(builder.AttachmentLayout.carousel) // This works
        .attachments(cards)
        .text('Choose a card')

      builder.Prompts.choice(session, msg, choices, {
        retryPrompt: msg
      })
    }, (session, args, next) => {
      session.send(args.response.entity)
      session.endDialogWithResult(args)
    }
  ])

Great Answer @benswinburne Thank you very much, but please note if you are going to use an Array of objects you had to create a string manually in builder.Prompts.choice(session, msg, choices, {
retryPrompt: msg
})
or you will get some "exec" Error
just create a string matching your hero card postback value

I want to give it some conversation after that it should return adaptive card and then based on input data(response) from that adaptive card, it should proceed further I am getting adaptive card but without getting any response it shows the further processes, it is not taking reponse from user for that too:

Can anyone help me with this?

   var card = {
        'contentType': 'application/vnd.microsoft.card.adaptive',
        'content': {
          "$schema": "http://adaptivecards.io/schemas/adaptive- 
           card.json",
          "type": "AdaptiveCard",
          "version": "1.0",
          "body": [{
            "type": "ColumnSet",
            "columns": [{
                "type": "Column",
                "width": 2,
                "items": [{
                    "type": "TextBlock",
                    "text": "Sign-in",
                    "weight": "bolder",
                    "size": "medium",
                    "horizontalAlignment": "center"
                  },
                  {
                    "type": "TextBlock",
                    "text": "Don't worry, we'll never share or sell your information.",
                    "isSubtle": true,
                    "wrap": true,
                    "size": "small"
                  },
                  {
                    "type": "TextBlock",
                    "text": "Username",
                    "wrap": true
                  },
                  {
                    "type": "Input.Text",
                    "id": "Username",
                    "placeholder": "Username"
                  },
                  {
                    "type": "TextBlock",
                    "text": "Password",
                    "wrap": true
                  },
                  {
                    "type": "Input.Text",
                    "id": "Password",
                    "placeholder": "********",
                    "style": "password"
                  }
                ]
              },
            ]
          }],
          "actions": [{
            "type": "Action.Submit",
            "title": "Submit",
            "data":{
              "x":13
          }
          }]
        }
      };

      var msg = new builder.Message(session)
        .addAttachment(card);
      session.send(msg);
      console.log("raw body212", card["content"]["actions"][0]["data"]);
      {
    var Username = card["content"]["actions"][0]["data"]["Username"];
    var Password = card["content"]["actions"][0]["data"]["Password"];
    if (Username = "abhishak" && (Password = "abhishak")) {
        console.log("raw body21", body);


     var purchase_payload = {
        .............................
      };

      ticket_purchase(purchase_payload, function (body) {
        console.log("raw body", body);
        session.send(
          "Your ticket has been purchased.Your available balance is " + body["data"]["availableBal"] + " and your ticket number is " + body["data"]["ticketData"]["ticketNumber"]
        );

        function createReceiptCard(session) {
          return new builder.ReceiptCard(session)
            .title(body["data"]["ticketData"]["gameName"], 'Game')
            .facts([
              builder.Fact.create(session, body["data"]["ticketData"]["ticketNumber"], 'Ticket Number'),
              builder.Fact.create(session, body["data"]["ticketData"]["playerPurchaseAmount"], 'Ticket Cost'),
              builder.Fact.create(session, body["data"]["saleTransId"], 'Sale Transaction Id'),
              builder.Fact.create(session, body["data"]["ticketData"]["purchaseTime"], 'Purchase Time'),
              builder.Fact.create(session, body["data"]["ticketData"]["gameName"], 'Game'),
              builder.Fact.create(session, body["data"]["ticketData"]["drawData"][0]["drawId"], 'Draw Id'),
              builder.Fact.create(session, body["data"]["ticketData"]["drawData"][0]["drawName"], 'Draw Name'),
              builder.Fact.create(session, body["data"]["ticketData"]["drawData"][0]["drawDate"], 'Draw Date'),
              builder.Fact.create(session, body["data"]["ticketData"]["drawData"][0]["drawTime"], 'Draw Time')
            ])
            .total(body["data"]["availableBal"], "Available Balance")
        }
        var card = createReceiptCard(session);
        console.log("aka2", card);
        var msg = new builder.Message(session).addAttachment(card);
        session.send(msg);

      });
    } else {
      session.send("Invalid Username/Password.");
    }
    }
Was this page helpful?
0 / 5 - 0 ratings