bolt-python icon indicating copy to clipboard operation
bolt-python copied to clipboard

Question regarding block format for images as uploaded files in messages

Open Yasin-Mtk opened this issue 8 months ago • 5 comments

Hi there, I apologize in advance if this is not the correct place to post about this question.

I would like to send multiple images in this format:

Image

(the above is just done by pasting two images in the chat-box and sending)

But the only way I have figured for this is using smth like this:

blocks= [
                {
                    "type": "section",
                    "text": {"type": "mrkdwn", "text":"TESTING"},
                },
                {
                    "type": "image",
                    "slack_file": {"id": file_ids[0]},
                    "alt_text": "Smile 1",
                },{
                    "type": "image",
                    "slack_file": {"id": file_ids[1]},
                    "alt_text": "Smile 2",
                },
                {"type": "divider"},
            ]

That produces this:

Image

And this doesn't suit my purpose. I played quite a bit with the https://app.slack.com/block-kit-builder/ , but I couldn't seem to figure it out. Any suggestions would be appreciated. Thanks!

Yasin-Mtk avatar May 21 '25 15:05 Yasin-Mtk

Hey @Yasin-Mtk! 👋 Thanks for sharing the example with this question - it's appreciated here 😄 ✨

A subtle difference is being shown in between the outputs of these two messages:

  1. Messages posted as files can appear inline as images
  2. Messages posted as image blocks will appear as "stacked" images

This might not match the exact formatting you're hoping to achieve, but I will recommend the files_upload_v2 method to upload images as files:

app.client.files_upload_v2(
    file_uploads=[
        {
            "file": "./smile.png",
            "title": "A glad face",
        },
        {
            "file": "./grin.png",
            "title": "A good feeling",
        },
    ],
    channel="C0123456789",
    initial_comment="Good morning!",
)

To inspect the Block Kit contents of a message, the Slack Developer Tools app can also be so useful I find! 📚

I'd love to know if this workaround is alright for you? I understand some limitations of files_upload_v2 might appear, but we can discuss workarounds if needed 👾

zimeg avatar May 21 '25 16:05 zimeg

Hi @zimeg, Thank you for your reply! I'll try out the dev tools in the future, didn't know about them.

So here is the code I currently use for uploading images:

async def upload_files_to_chat(self, file_paths : list[str], thread_ts : str = None, message_to_include : str = None, reply_broadcast : bool = None) -> None:
        """
        Uses files_upload_v2 to upload a file to a channel,
        Makes sure file is uploaded successfully,
        Polls file status until it's ready,
        Sends file to channel using chat_postMessage,
        """
        # Info on why this function is as is:
        # https://github.com/slackapi/python-slack-sdk/releases/tag/v3.19.0
        # https://api.slack.com/changelog/2024-04-a-better-way-to-upload-files-is-here-to-stay
        # https://github.com/slackapi/python-slack-sdk/issues/1658

        s = time.perf_counter()
        # files_upload_v2 does not allow blocks, so you need to first upload the file to get a file id
        # then poll slack to wait until file is fully processed and then use chat_postMessage to send the message
        for _ in range(self.config["slack"]['max_file_upload_attempts']):
            file_upload_response = await app.client.files_upload_v2(
                    # initial_comment="sample msg",
                    file_uploads=[{"file": file} for file in file_paths],
                )
            LOGGER.debug(f"File status response {pp(file_upload_response.data)}\n\n")
            LOGGER.debug("****************************************")
            if file_upload_response['ok']:
                break
            await asyncio.sleep(0.5)
        else:
            LOGGER.error(f"Error uploading file {file_upload_response}")
            raise Exception(f"Error uploading file {file_upload_response}")
        
        file_ids = [file_info['id'] for file_info in file_upload_response['files']]
        # Poll file status, until it's ready
        for id in file_ids:
            for _ in range(self.config["slack"]['max_file_polling_attempts']):
                file_status_response = await app.client.files_info(
                    file=id,
                )
                LOGGER.debug(f"File status response {file_status_response}\n\n")
                if len(file_status_response['file']['filetype']) != 0:
                    LOGGER.info(f"File uploaded to slack successfully")
                    break
                await asyncio.sleep(0.5)
            else:
                LOGGER.error(f"File status shows file is not ready for use after {self.config['slack']['max_file_polling_attempts']} attempts {file_status_response}")
                raise Exception(f"Error checking file status {file_status_response}")

        txt_block = []
        if message_to_include is not None:
            txt_block = [{
                    "type": "section",
                    "text": {"type": "mrkdwn", "text": message_to_include},
                }]

        await app.client.chat_postMessage(
            channel=self.config['slack']['channel_id'],
            thread_ts=thread_ts,
            reply_broadcast=reply_broadcast,
            text="Snippy Chart",
            blocks= txt_block + [
                {
                    "type": "image",
                    "slack_file": {"id": file_ids[0]},
                    "alt_text": "Snippy Chart",
                },{
                    "type": "image",
                    "slack_file": {"id": file_ids[1]},
                    "alt_text": "Snippy Chart",
                },
                {"type": "divider"},
            ],
        )
        LOGGER.info(f"Uploading graph took {time.perf_counter() - s} seconds")
        return

You can look at https://github.com/slackapi/python-slack-sdk/issues/1658 for the details, But in summary the reason I can't just rely on files_upload_v2 to send the files is

  • I would like the message to be a reply in a thread
  • I would like to have control over reply_broadcast

That's why I use files_upload_v2 to upload then I poll file status to make sure file/image is ready, then use chat_postMessage.

So ideally if you know of a way to include file blocks in the msg OR A way to have the extra control over threads and reply_broadcast.

Thx for your help!

Yasin-Mtk avatar May 22 '25 14:05 Yasin-Mtk

@Yasin-Mtk 🤔 Hmm... This might be pushing the limits of what's now possible with files_upload_v2 I fear.

AFAIK the exact formatting outputs of image blocks can't be specified more than in Block Kit and the file block is limited to external files.

Uploading these images as files and sharing the permalink in a message might unlock more formatting options, though blocks shown in the above examples might be recommended for sending threaded and broadcasted messages.

Please let me know if the image block approach at least offers a possible method for posting formatted messages 💌

Changes to outputs more might have to be routed through a message to [email protected] since this isn't something we can update on the SDK side of things, and this sends a signal to the right teams that this is a feature to prioritize!

zimeg avatar May 23 '25 02:05 zimeg

@zimeg So I tried to only use files_upload_v2 uploading the images as files & setting the channel id & thread id It looks exactly how I want, but the only issue is that it doesn't support using reply_broadcast, and the issue boils down to the files.completeUploadExternal api call not taking that parameter.

I'm sure this would be an easy improvement to implement, do you think this would be a reasonable feature request to forward towards slack?

Yasin-Mtk avatar May 23 '25 15:05 Yasin-Mtk

@Yasin-Mtk I do think it's a reasonable request, but we must suggest it goes through a collective API decision process 🙏

In the meantime would it be possible to use the chat.update method with a changed value for reply_broadcast?

Polling for a successful upload with conversations.replies might be required and I understand this requires additional scopes, but no other workaround is coming to mind for me right now 🤔

zimeg avatar May 28 '25 02:05 zimeg

👋 It looks like this issue has been open for 30 days with no activity. We'll mark this as stale for now, and wait 10 days for an update or for further comment before closing this issue out. If you think this issue needs to be prioritized, please comment to get the thread going again! Maintainers also review issues marked as stale on a regular basis and comment or adjust status if the issue needs to be reprioritized.

github-actions[bot] avatar Jun 30 '25 00:06 github-actions[bot]

As this issue has been inactive for more than one month, we will be closing it. Thank you to all the participants! If you would like to raise a related issue, please create a new issue which includes your specific details and references this issue number.

github-actions[bot] avatar Jul 14 '25 00:07 github-actions[bot]