4

I'm having some issues to update an interactive message after responding to a slack dialog. I'm using botkit on a node.js server.

Here is my workflow:

  1. User trigger an interactive message via a slash command
  2. User click a button on that message
  3. A dialog pops up, user fill the form and validate
  4. Something is done on the server side
  5. The first message should update

Now, here is the logic I'm using:

  1. User trigger an interactive message via a slash command

Nothing fancy, I use:

controller.on('slash_command', function (bot, message)

Then I parse the command, and send the appropriate message, with the appropriate attachments (buttons)

  1. User click a button on that message

Same, I use the event sent by botkit:

controller.on('interactive_message_callback', function (bot, message)

Then I create a dialog:

var dialog = bot.createDialog(
                        'Which book?',
                        JSON.stringify(callback),
                        'Ok'
                    )

Here I'm doing something really (really) dirty, and should not be done. But that's the only way I found to update the initial message after the dialog is filled. The callback_id actually contains an object, with the response_urlof the initial message (and something to identify the form).

  1. A dialog pops up, user fill the form and validate
  2. Something is done on the server side

Here, I use once more the event provided by botkit:

controller.on('dialog_submission', function (bot, message)

then I parse the message.submission.callback_id and detect the response_url. With this, I can create an object I call originalMessage.

  1. The first message should update

At the moment I use :

bot.replyInteractive(originalMessage, 'DONE, everything is saved.');

with originalMessagecontaining the response_url of the first message. It does work. The first message is being replaced by the new one.

But I'm really not happy with that solution, and was wondering if I was missing something somewhere. I've seen couple apps having that type of workflow, so there must be a way.

Thank you for your help :)

RomOne
  • 2,065
  • 17
  • 29
  • did you figure out a way of doing this? I'm sorta stuck with the same thing, wanting to tidy up the original message that triggered the dialog that triggered the dialog_submission event. I thought I could just replyInteractive with delete_original, but there's no reference to the original question message... – Tiago Oct 24 '17 at 16:07
  • Unfortunately I'm still storing the `response_url` in `callback_id`. Basically, once you have the url of the originalMessage, you can use `replyInteractive` with a new object where you stick your `response_url`, the team, and user. That's what I'm doing and it works. It's just really dirty way... – RomOne Oct 25 '17 at 05:12
  • Wow that does feel dirty. The thought I have right now is that I remove my message after the button that opens the dialog is clicked. Then when the dialog is done and some action has finished processing, I add a new message or whisper to reflect that. Will need to experiment if that feels clean. Thanks @RomOne – Tiago Oct 25 '17 at 16:15
  • Yep, I thought the same actually. Seems much better! The only thing is that if the user press cancel, they will end up with nothing. – RomOne Oct 26 '17 at 03:43
  • Yeah that's the drawback I still dislike. They'd have to prompt the bot workflow again. I checked in the slack API channel, looks like the cancel button doesn't have a callback. Not sure how to handle that yet. – Tiago Oct 27 '17 at 08:34

3 Answers3

3

Slack has now changed their API and the way to persist data with the new Modals is a little different.

When a dialog is submitted, your app receives an HTTP request. dialog_submission events contained a response_url that allowed you to post a message tied to the channel the dialog was initiated from. However, modals don't rely on a channel context. This means that the view_submission event will not contain a response_url.

If you want to persist data when opening a Modal, you can pass whatever data you want to persist in a private_metadata field.

Whatever data you passed will be returned to you on the modal submission callback.

Crisp Apples
  • 267
  • 1
  • 2
  • 13
1

I wrote to Slack to ask about this situation and got a great suggestion from Mark P:

Use the state dialog field to pass the original response_url to the dialog. Then when you receive the dialog data, you can use state instead of response_url.

I just tried it and it worked great. No need to store any state on your own server.

I don't know how that would work exactly with Node and botkit, since that's not what I use.

To flesh this out a bit more:

  1. Someone clicks a button and Slack POSTs about that interaction to your configured "Request URL".
  2. From Slack's payload, get the "response_url" value.
  3. When you call dialog.open in the Slack API, pass along this response_url as the "state" value.
  4. When the dialog is submitted, Slack again POSTs to your "Request URL".
  5. From Slack's payload, get the "state" value and use it as a response_url.
  6. Profit!
Henrik N
  • 15,786
  • 5
  • 82
  • 131
  • 1
    nice! thanks really much for that one, I was still using my workaround, and that one seems waaay cleaner. Thanks again – RomOne Mar 25 '19 at 11:08
  • 1
    If anyone is reading this in 2023 the state parameter has been changed to private_metadata in Slack's new modal feature that replaces dialogs. – Crisp Apples Apr 03 '23 at 02:19
0

This only works if you hold the original message object somewhere on your server for future reference.

So on creating the interactive dialog store it somewhere and add a reference. I use uuids.

    let newId = uuid();
    messageStore[newId] = message;
    var dialog = bot.createDialog(
        'My Dialog',
        'idPrefix_' + newId,
        'Submit'
    ).addText('Sample Input', 'input', '');
    bot.replyWithDialog(message, dialog.asObject());

Then once you get your interactive dialog response back disassemble the prefix and the uuid and get your original message object back from the servers memory. Then use ´replayInteractive` there.

controller.on('dialog_submission', function handler(bot, message) {
    if (message.callback_id.indexOf('idPrefix') === 0) {
       let id = message.callback_id.substr('idPrefix_'.length);
       bot.dialogOk();
       let originalMessage = messageStore[id];
       bot.replyInteractive(originalMessage, {
            text: 'replacing the original message with this.'
       });
    }
});

Be careful that you do not create a memory leak here. You have to find a way to clean up your messageStore over time.

MaxM
  • 625
  • 7
  • 13