Search code examples
pythonslackslack-api

Sending message with Slack WebClient that includes an uploading image


I'm trying to use the Slack Web Client to send a message from a bot to a private channel. The message would include some text and an image. After reading the current Slack documentation, it seems like the best way to accomplish this would be to use the file.upload method to upload the file to Slack, and then use the chat.PostMessage method to send the message including a URL to the hosted image. While it seems that I'm able to upload the file, when I go to send the message, I get an error regarding the file that I've uploaded. I'm not sure if I'm passing the wrong URL or if there is something else that I need to do after uploading the image. I'm able to successfully send a message without a file, so I know the issue has to do with the image specifically.

Error: The request to the Slack API failed.
The server responded with: {'ok': False, 'error': 'invalid_blocks', 'errors': ['downloading image failed [json-pointer:/blocks/1/image_url]'], 'response_metadata': {'messages': ['[ERROR] downloading image failed [json-pointer:/blocks/1/image_url]']}}

Below is the process that I'm using to create the web client, upload the file, then send the message.

import os
import requests
from slack_sdk import WebClient
from slack_sdk.errors import SlackApiError
from pprint import pprint

# create Slack web client
client = WebClient(token="xoxb-123456789")

# find the IDs of the Slack channels
for result in client.conversations_list():
    for channel in result["channels"]:
        if channel['name'] == 'my_channel':
            channel_id = channel['id']
            break

# upload image to my Slack channel
image = client.files_upload(
    channel = channel_id,
    initial_comment = "This is my image",
    file = "~/image.png"
)

# write my message
block = [
    {
        "type": "section",
        "text": {
            "type": "mrkdwn",
            "text": "Guess what?  I don't know"
            }
    },
    {
            "type": "image",
            "image_url": image['file']['permalink'],
            "alt_text": "inspiration"
    } 
]

# try to send message with image
try:
    result = client.chat_postMessage(
        channel = channel_id,
        text = "New message for you",
        blocks = block
    )
    
except SlackApiError as e:
    print(f"Error: {e}")

At this point, I experience the following error message:

Error: The request to the Slack API failed.
The server responded with: {'ok': False, 'error': 'invalid_blocks', 'errors': ['downloading image failed [json-pointer:/blocks/1/image_url]'], 'response_metadata': {'messages': ['[ERROR] downloading image failed [json-pointer:/blocks/1/image_url]']}}

For the purpose of troubleshoot, here is the data that I get back

# print the details about the file uploaded
pprint(image['file'])

{'channels': [],
 'comments_count': 0,
 'created': 1648070852,
 'display_as_bot': False,
 'editable': False,
 'external_type': '',
 'filetype': 'png',
 'groups': [],
 'has_rich_preview': False,
 'id': 'FHBB87462378',
 'ims': [],
 'is_external': False,
 'is_public': False,
 'is_starred': False,
 'media_display_type': 'unknown',
 'mimetype': 'image/png',
 'mode': 'hosted',
 'name': 'image.png',
 'original_h': 1004,
 'original_w': 1790,
 'permalink': 'https://sandbox.enterprise.slack.com/files/123456789/ABC/image.png',
 'permalink_public': 'https://slack-files.com/123456789',
 'pretty_type': 'PNG',
 'public_url_shared': False,
 'shares': {},
 'size': 1623063,
 'thumb_1024': 'https://files.slack.com/files-tmb/123456789/image_1024.png',
 'thumb_1024_h': 574,
 'thumb_1024_w': 1024,
 'thumb_160': 'https://files.slack.com/files-tmb/123456789/image_160.png',
 'thumb_360': 'https://files.slack.com/files-tmb/123456789/image_360.png',
 'thumb_360_h': 202,
 'thumb_360_w': 360,
 'thumb_480': 'https://files.slack.com/files-tmb/123456789/image_480.png',
 'thumb_480_h': 269,
 'thumb_480_w': 480,
 'thumb_64': 'https://files.slack.com/files-tmb/123456789/image_64.png',
 'thumb_720': 'https://files.slack.com/files-tmb/123456789/image_720.png',
 'thumb_720_h': 404,
 'thumb_720_w': 720,
 'thumb_80': 'https://files.slack.com/files-tmb/123456789/image_80.png',
 'thumb_800': 'https://files.slack.com/files-tmb/123456789/image_800.png',
 'thumb_800_h': 449,
 'thumb_800_w': 800,
 'thumb_960': 'https://files.slack.com/files-tmb/123456789/image_960.png',
 'thumb_960_h': 538,
 'thumb_960_w': 960,
 'thumb_tiny': 'AoinfgvoindwoidnasQOJWQNWOIQONQqoinoiQQ/2Q==',
 'timestamp': 1648070852,
 'title': 'image',
 'url_private': 'https://files.slack.com/files-pri/123456789/image.png',
 'url_private_download': 'https://files.slack.com/files-pri/123456789/download/image.png',
 'user': 'U123456789',
 'username': ''}

Solution

  • I found out that you need to have the top-level text property in addition to the blocks. The example below works as expected and now I'm able upload an image to Slack and the include that image in a message.

    See https://github.com/slackapi/python-slack-sdk/issues/1194 for more info.

    # get the file URL
    file_url = image["file"]["permalink"]
    
    # write my message
    block = [
        {
            "type": "section",
            "text": {
                "type": "mrkdwn",
                "text": "Guess what?  I don't know"
                }
        }
    ]
    
    # try to send message with image
    try:
        result = client.chat_postMessage(
            channel = channel_id,
            text = f"Here is the image data that you want! {file_url}",
            blocks = block
        )
        
    except SlackApiError as e:
        print(f"Error: {e}")