pythonslack-api

how to share/upload files in slack?


Based on the documentation provided here -> https://api.slack.com/tutorials/tracks/uploading-files-python#upload-file. I can upload a file to slack , via python slack sdk (sample code below). I noticed the channel id is optional in the call below. in the response, there is url_private field to the file. to share this or make it public. the api has another method -> files.sharedPublicURL. but there is no example of this , on how to call this api method.

my first question is has anyone tried to make a file public. another question I have is , if I add a Channel id to the file upload call, where does the file live and for how long? is the time configurable. say after 30 days file is deleted.

also , if I wanted to share this file , in other channels or in direct message to user programatically. is that possible?

request

new_file = client.files_upload_v2(
    title="My Test Text File",
    filename="test.txt",
    content="Hi there! This is a text file!",
)


response

{
    "ok": true,
    "file": {
        "id": "F0TD00400",
        "name": "dramacat.gif",
        "title": "dramacat",
        "mimetype": "image/jpeg",
        "filetype": "gif",
        "pretty_type": "JPEG",
        "user": "U0L4B9NSU",
        "editable": false,
        "size": 43518,
        "mode": "hosted",
        "is_external": false,
        "external_type": "",
        "is_public": false,
        "public_url_shared": false,
        "display_as_bot": false,
        "username": "",
        "url_private": "https://.../dramacat.gif",
        "url_private_download": "https://.../dramacat.gif",
        ...

Solution

  • The method client.files_upload_v2() will be deprecated, and the Slack documentation you refer to unfortunately does not mention this.

    New Slack Bots/Apps are unable to access this method, and this method will be deprecated entirely next year. If you have not created your bot in March (as far as I know) of this year, likely you will not be able to use this method anymore.

    But there is a new way of doing things, documented here and here. This should work:

    from slack_sdk import WebClient
    
    import base64
    import os, io
    
    import requests
    
    from io import BytesIO
    from PIL import Image
    from IPython.display import display
    
    message = "Test upload"
    example_base64_image = "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"
    image_data = base64.b64decode(example_base64_image)
    image = Image.open(BytesIO(image_data))
    with BytesIO() as buffer:
        image.save(buffer, format=image.format)  # Save the image to the buffer
        image_size_in_bytes = buffer.tell()  # Get the size in bytes
        print(f"image_size_in_bytes of file {type(image)}: {image_size_in_bytes}")
    img_byte_arr = io.BytesIO()
    image.save(img_byte_arr, format='PNG')
    img_byte_arr = img_byte_arr.getvalue()
    attachment = img_byte_arr
    
    slack_channel_id = "Some correct channel ID (not the channel name)"
    slack_token = "Some correct token"
    slack_client = WebClient(token=slack_token)
    
    
    # Step 1/4: Get the URL to upload to.
    attachment_size = len(attachment)
    url_for_uploading = slack_client.files_getUploadURLExternal(
        token=slack_token,
        filename="Attachment",
        length=attachment_size,
    )
    
    if url_for_uploading["ok"]:
        for item in url_for_uploading:
            print(f"{item}")
    else:
        raise ValueError(
            f"Failed to get the URL for uploading the attachment to Slack! Response: {url_for_uploading}"
        )
    
    # Step 2/4: Upload the file to the URL.
    payload = {
        "filename": "attachment.jpeg",
        "token": slack_token,
        # "channels": ["#dsc-dbx-alerts-test"],
    }
    response = requests.post(
        url_for_uploading["upload_url"], params=payload, data=attachment
    )
    
    if response.status_code == 200:
        print(
            f"Response from Slack: {response.status_code}, {response.text}"
        )
    else:
        raise ValueError(
            f"Response from Slack: {response.status_code}, {response.text}, {response.headers}"
        )
    
    file_id = url_for_uploading["file_id"]
    
    # Step 3/4: Make the file accessible in the channel.
    slack_client.files_completeUploadExternal(
        token=slack_token,
        files=[{"id": file_id, "title": "Attachment"}],
        channel_id=slack_channel_id,
        initial_comment=None,
        thread_ts=None,
    )
    
    attachment_with_slack_url = {
        "title": "Attachment",
        "image_url": url_for_uploading["upload_url"],
    }
    
    # Step 4/4: Send the message to the specified Slack channel.
    response = slack_client.chat_postMessage(
        channel=slack_channel_id,
        text=message,
        attachments=[attachment_with_slack_url],
    )
    
    # Check if message sending was successful.
    if response.status_code != 200:
        raise ValueError(
            f"Failed to send the message to Slack! Status code returned from the Slack API: {response.status_code}"
        )
    
    

    Your Slack Bot will need sufficient access rights ("scopes"). You can set them here, just click on your bot, scroll down, and provide it with the necessary scopes.