slackapi / bolt-js

A framework to build Slack apps using JavaScript
https://tools.slack.dev/bolt-js/
MIT License
2.74k stars 393 forks source link

Links unfurl twice #1525

Closed EddyYeung1 closed 2 years ago

EddyYeung1 commented 2 years ago

Description

I am using the chat.unfurl method to create custom unfurls, however it seems to unfurl twice when I post the message. One through my implementation and another through the classic link unfurling. In the documentation it says

Be sure and respond with a friendly HTTP 200 OK to the event as quickly as possible. Do not wait to wrestle an unfurl with [chat.unfurl](https://api.slack.com/methods/chat.unfurl) before telling Slack you received the event. You'll probably want to enqueue behavior like this.

Looking at the Bolt Python Repo issues one person mentions this: https://github.com/slackapi/bolt-python/issues/283#issuecomment-815354541 as well.

I think the double unfurling may be happening because we don't acknowledge the request, however the ack() function is not in the slack event middleware arguments.

Screen Shot 2022-07-20 at 12 34 56 PM

What type of issue is this? (place an x in one of the [ ])

Requirements (place an x in each of the [ ])


Bug Report

Reproducible in:

package version: "@slack/bolt": "3.11.1",

node version: 14.9

OS version(s): macOS Monterey 12.4

Steps to reproduce:

Probably will need to download our Guru QA app if that is possible paste link in message box https://qaapp.getguru.com/card/cMEMgbbi/Public-Card-Test

Expected result:

only one unfurl

Screen Shot 2022-07-20 at 12 45 46 PM

Actual result:

two unfurls

Screen Shot 2022-07-20 at 12 46 13 PM

Attachments:

This is the flow our code for listening to chat unfurls.

in our index we listen to the "link_shared" event app.event("link_shared", handleLinkUnfurls);

handleLinkUnfurls handles the event

export async function handleLinkUnfurls(
  props: LinkUnfurlParams
): Promise<void> {
  const { client, event, context } = props; //no ack function available

  const ts = event.message_ts;
  const channel = event.channel;
  const url = event.links[0].url;

  //business logic code 

  const unfurls = {
    [url]: {
      blocks: createLinkUnfurlView(cardData, url),
      preview: {
        title: {
          type: "plain_text",
          text: "card title"
        }
      }
    }
  };

  await client.chat.unfurl({ ts, channel, unfurls });
srajiang commented 2 years ago

Hey @EddyYeung1! Thanks for writing this up and providing helpful attachments and steps to reproduce. I've run out of time today to take a look at this issue, but it's on my radar to look at first thing tomorrow PST.

seratch commented 2 years ago

I am happy to jump in here as I have some knowledge on this topic and I live in a different timezone 🙌

Hi @EddyYeung1 , this might be confusing and it should be clearly mentioned in the documents but you don't need to manually call ack() method for Events API data patterns. bolt-js automatically does ack() for you. As long as your bolt-js app successfully receives the link_shared events, the acknowledgement should be done under the hood. If you see a different situation such as retried event deliveries, let us know the details of your code and conditions.

EddyYeung1 commented 2 years ago

@seratch ok in that case, what might be causing the links to unfurl twice? I was hoping that if we have the domain registered in the slack dev portal slack would recognize not to fallback to classic link unfurling?

seratch commented 2 years ago

@EddyYeung1

I was hoping that if we have the domain registered in the slack dev portal slack would recognize not to fallback to classic link unfurling?

Sorry, we don't have such an option at this moment.

If your app receives link_shared events multiple times and it's not due to retries, I'd suggest checking if the user_id / bot_user_id in the event payload is the one of your app to skip repeating the chat.unfurl API requests.

EddyYeung1 commented 2 years ago

@seratch interesting I found this part of the documentation as well, but I am not sure where to put this to turn off classic link unfurling?

seratch commented 2 years ago

@EddyYeung1 Ah, I see the point. These flags can be passed when you post a message using chat.postMessage / chat.postEphemeral / chat.scheduleMessage API methods.

EddyYeung1 commented 2 years ago

@seratch so if we use chat.unfurl we can't turn it off at all and just have to manually close out the slack classic unfurl?

seratch commented 2 years ago

@EddyYeung1 I do understand that the lack of those options is an obstacle for your use case, but your understanding is correct.

EddyYeung1 commented 2 years ago

@seratch Just for clarification in the slack docs it says "If a workspace doesn't have a Slack app handler for a specific domain, unfurling will fall back to classic behavior: " Wouldn't this mean slack will ONLY fall back to classic unfurling IF the workspace doesn't have the specific domain registered?

seratch commented 2 years ago

In this case, it seems that your unfurling content itself also has the target URL in the blocks. So, my understanding of your situation is that the default unfurling for the URL in your blocks happens while your custom unfurling works.

EddyYeung1 commented 2 years ago

we actually thought the same thing and tested it without the context block having our link in it. As you can see in this screenshot the behavior still persists.

Screen Shot 2022-07-20 at 11 21 12 AM
seratch commented 2 years ago

@EddyYeung1 Thanks for your reply. I confirmed that I could see the same behavior on my end. Even when the blocks in a chat.unfurl API request do not contain any URLs, the classic unfurling occurs.

We will escalate this feedback to the product / engineering teams. However, as for the short-term solution, AFAIK, there is no workaround at this moment. Also, since I had misunderstood the latest behavior of unfurling in this scenario, I couldn't provide a quick answer to this inquiry. I am sorry for that.

As the SDK maintainers, we don't have any further actions that we can take for you in this issue tracker. So please let us close this issue now. Please contact our partnership counterparts and customer support agents if you have any follow-ups on the unfurling behavior.

EddyYeung1 commented 2 years ago

@seratch Hey thanks for looking into this for me. I actually just discovered that the link in our Action Button was the reason it was unfurling twice. I removed it and it no longer classically unfurled. However, this is still an issue because we have to include that URL in the button for it to work. So still seems like a slack issue, but at least we know where it's coming from now. It is odd however, on your end you were getting the same behavior for having no links present.

srajiang commented 2 years ago

@EddyYeung1 - I think it should be a bug if Slack tries to unfurl links inside of existing unfurl content supplied as part of chat.unfurl. I'll keep this issue closed as I agree with @seratch that there's not much we can do as SDK maintainers to help resolve this on our end.

However, I have filed an issue with the interactivity team on our end. As I get responses, I will update!

srajiang commented 2 years ago

@EddyYeung1

that the link in our Action Button was the reason it was unfurling twice.

I noticed there are two buttons in the blocks. Does that Action Button link you mentioned contain the same URL as the URL which triggers the link_shared event in the first place (aka the one in the message)? Or is it a different URL?

EddyYeung1 commented 2 years ago

@srajiang its the same URL, oddly enough however, we are no longer having the issue anymore. We haven't made any changes

srajiang commented 2 years ago

Ok. First, I'm glad you're not having the issue anymore - but how frustrating that we weren't able to get to the bottom of why. I'm sorry about that! It's definitely a Slack server-side behavior, I am just not sure if a fix is likely to materialize soon, since it's seen inconsistently and those are difficult to get fixes for.

Here's what I've been able to find out, if it helps:

A known behavior we've seen: The original link shared unfurls twice – once by Guru (via chat.unfurl) and once by the default Slack unfurler. The server-side does it's best to block the in-built unfurl, but if the app takes a little while to respond, timing issues can happen. This sounds a bit like what Kaz described repro-ing since he saw classic unfurl behavior with no additional links shared. This is unfortunately something that isn't going to be fixed any time soon.

Still doesn't fully explain why removing the second link would temporarily fix the unfurling issue, so I'll poke around a bit more. Let us know whether the issue comes back @EddyYeung1!

srajiang commented 2 years ago

@EddyYeung1 - There's one last thing I want to check. Can you provide your App ID and the Team ID of the workspace you're developing in?

EddyYeung1 commented 2 years ago

@srajiang App ID: A0321PHS5BJ Team ID: T031941HY69

rjkward commented 1 year ago

I found this thread after having the same issue myself. In my case what fixed it was adding the links:write scope to the oauth configuration page: https://api.slack.com/apps/{YOUR_APP_ID}/oauth

This is in addition to the stuff you need to configure around events/subs.

It's confusing because you can still generate a valid token through OAuth with the required scopes as query params without having the scopes added to the config on that page. Hopefully this will save someone a bit of time in the future.