Paulo Salgado
Paulo Salgado

Reputation: 487

Send multiple files to Slack via API

According to Slack's documentation is only possible to send one file per time via API. The method is this: https://api.slack.com/methods/files.upload.

Using Slack's desktop and web applications we can send multiple files at once, which is useful because the files are grouped, helping in the visualization when we have more than one image with the same context. See the example below:

enter image description here

Does anyone know if it's possible, via API, to send multiple files at once or somehow achieve the same results as the image above?

Thanks in advance!

Upvotes: 22

Views: 10310

Answers (8)

Swamp Fox
Swamp Fox

Reputation: 11

Here is how I did the same thing using Go via the slack-go/slack library:

func uploadImagesToSlack(images []string, token string) ([]string, error) {
  api := slack.New(token)
  permalinkList := make([]string, 0, len(images))

  for i, base64Image := range images {
    imageData, err := base64.StdEncoding.DecodeString(base64Image)
    if err != nil {
        fmt.Printf("Error decoding image %d: %v\n", i, err)
        continue
    }

    params := slack.FileUploadParameters{
        Filename: fmt.Sprintf("image_%d.png", i+1),
        Filetype: "auto",
        Title:    fmt.Sprintf("Image %d", i+1),
        Content:  string(imageData),
    }
    fileInfo, err := api.UploadFile(params)
    if err != nil {
        fmt.Printf("Error uploading file for image %d: %v\n", i+1, err)
        continue
    }

    permalinkList = append(permalinkList, fileInfo.Permalink)

}
return permalinkList, nil

}

func postGalleryToSlack(permalinkList []string, channel, token string) error {
  api := slack.New(token)

  messageText := ""

  for _, permalink := range permalinkList {
    textSingleLink := fmt.Sprintf("<%s | >", permalink)

    messageText = messageText + textSingleLink
  }
  channelID, timestamp, err := api.PostMessage(
    channel,
    slack.MsgOptionText(messageText, false),
  )
  if err != nil {
    return err
  }
  fmt.Printf("Message successfully sent to channel %s at %s\n", channelID, timestamp)
  return nil

}

The images were retrieved in a previous function and then encoded into base64 strings using base64.StdEncoding.EncodeToString(), then this function is called which loops over the strings, decodes them, uploads to slack without posting, grabs the permalinks from the api.UploadFile() method, then posts them all in a single message which gives us the gallery format.

Upvotes: 0

user1405485
user1405485

Reputation: 31

it is possible with files_upload_v2

import slack_sdk
client = slack_sdk.WebClient(token=SLACK_TOKEN)
file_uploads = []

for file in list_of_files:
    file_uploads.append({
         'file' : file['path'],
         'filename' : file['name'],
         'title'  : file['title']
    })

new_file = client.files_upload_v2(
                   file_uploads=file_uploads,                                                         
                   channel=message['channel'],  
                   initial_comment=text_message
                )

Upvotes: 3

xanatos
xanatos

Reputation: 190

Here is an implementation of the procedure recommended in the other answer in python

def post_message_with_files(message, file_list, channel):
    import slack_sdk

    SLACK_TOKEN = "slackTokenHere"
    client = slack_sdk.WebClient(token=SLACK_TOKEN)
    for file in file_list:
        upload = client.files_upload(file=file, filename=file)
        message = message + "<" + upload["file"]["permalink"] + "| >"
    out_p = client.chat_postMessage(channel=channel, text=message)


post_message_with_files(
    message="Here is my message",
    file_list=["1.jpg", "1-Copy1.jpg"],
    channel="myFavoriteChannel",
)

Upvotes: 8

Nikita Mendelbaum
Nikita Mendelbaum

Reputation: 808

Python solution using new recommended client.files_upload_v2 (tested on slack-sdk-3.19.5 on 2022-12-21):

import slack_sdk


def slack_msg_with_files(message, file_uploads_data, channel):
    client = slack_sdk.WebClient(token='your_slack_bot_token_here')
    upload = client.files_upload_v2(
        file_uploads=file_uploads_data,
        channel=channel,
        initial_comment=message,
    )
    print("Result of Slack send:\n%s" % upload)


file_uploads = [
    {
        "file": path_to_file1,
        "title": "My File 1",
    },
    {
        "file": path_to_file2,
        "title": "My File 2",
    },
]
slack_msg_with_files(
    message='Text to add to a slack message along with the files',
    file_uploads_data=file_uploads,
    channel=SLACK_CHANNEL_ID  # can be found in Channel settings in Slack. For some reason the channel names don't work with `files_upload_v2` on slack-sdk-3.19.5
)

(some additional error handling would not hurt)

Upvotes: 8

Kim K
Kim K

Reputation: 39

Simply use Slack Blocks: Block Kit Builder. Great feature for the message customizations.

Upvotes: -1

Philip
Philip

Reputation: 51

For Python you can use:

permalink_list = []
file_list=['file1.csv', 'file2.csv', 'file3.csv']
for file in file_list:
    response = client.files_upload(file=file)
    permalink = response['file']['permalink']
    permalink_list.append(permalink)

text = ""
for permalink in permalink_list:
    text_single_link = "<{}| >".format(permalink)
    text = text + text_single_link
response = client.chat_postMessage(channel=channelid, text=text)

Here you can play around with the link logic - Slack Block Kit Builder

Upvotes: 1

Chris Hayes
Chris Hayes

Reputation: 13696

A Node.JS (es6) example using Slack's Bolt framework

import pkg from '@slack/bolt';
const { App } = pkg;
import axios from 'axios'

// In Bolt, you can get channel ID in the callback from the `body` argument
const channelID = 'C000000'
// Sample Data - URLs of images to post in a gallery view
const imageURLs = ['https://source.unsplash.com/random', 'https://source.unsplash.com/random']

const uploadFile = async (fileURL) {
  const image = await axios.get(fileURL, { responseType: 'arraybuffer' });

  return await app.client.files.upload({
    file: image.data
    // Do not use "channels" here for image gallery view to work
  })
}

const permalinks = await Promise.all(imageURLs.map(async (imageURL) => {
  return (await uploadImage(imageURL)).file.permalink
}))

const images = permalinks.map((permalink) => `<${permalink}| >`).join('')
const message = `Check out the images below: ${images}`

// Post message with images in a "gallery" view
// In Bolt, this is the same as doing `await say({`
// If you use say(, you don't need a channel param.
await app.client.chat.postMessage({
  text: message,
  channel: channelID,
  // Do not use blocks here for image gallery view to work
})

The above example includes some added functionality - download images from a list of image URLs and then upload those images to Slack. Note that this is untested, I trimmed down the fully functioning code I'm using.

Slack's image.upload API docs will mention that the file format needs to be in multipart/form-data. Don't worry about that part, Bolt (via Slack's Node API), will handle that conversion automatically (and may not even work if you feed it FormData). It accepts file data as arraybuffer (used here), stream, and possibly other formats too.

If you're uploading local files, look at passing an fs readstream to the Slack upload function.

Upvotes: 1

Roman Kazakov
Roman Kazakov

Reputation: 753

I've faced with the same problem. But I've tried to compose one message with several pdf files.

How I solved this task

  1. Upload files without setting channel parameter(this prevents publishing) and collect permalinks from response. Please, check file object ref. https://api.slack.com/types/file. Via "files.upload" method you can upload only one file. So, you will need to invoke this method as many times as you have files to upload.
  2. Compose message using Slack markdown <{permalink1_from_first_step}| ><{permalink2_from_first_step}| > - Slack parse links and automatically reformat message

Upvotes: 17

Related Questions