11

I've been searching a lot and I haven't found an answer to what I'm looking for.

I'm trying to upload a file from /tmp to slack using python requests but I keep getting {"ok":false,"error":"no_file_data"} returned.

file={'file':('/tmp/myfile.pdf', open('/tmp/myfile.pdf', 'rb'), 'pdf')}
payload={
        "filename":"myfile.pdf", 
        "token":token, 
        "channels":['#random'], 
        "media":file
        }

r=requests.post("https://slack.com/api/files.upload", params=payload)

Mostly trying to follow the advice posted here

Community
  • 1
  • 1
arshbot
  • 12,535
  • 14
  • 48
  • 71
  • I am not seeing a `media` param in the API docs. Try changing `"media":file` to `"content":open('/tmp/myfile.pdf', 'r').read()` – jordanm Apr 18 '17 at 06:20
  • This returns a 413 Error and `r.text` returns an HTML file that reads `The request could not be satisfied. Bad request` – arshbot Apr 18 '17 at 14:53

3 Answers3

29

Sending files through http requires a bit more extra work than sending other data. You have to set content type and fetch the file and all that, so you can't just include it in the payload parameter in requests.

You have to give your file information to the files parameter of the .post method so that it can add all the file transfer information to the request.

my_file = {
  'file' : ('/tmp/myfile.pdf', open('/tmp/myfile.pdf', 'rb'), 'pdf')
}

payload={
  "filename":"myfile.pdf", 
  "token":token, 
  "channels":['#random'], 
}

r = requests.post("https://slack.com/api/files.upload", params=payload, files=my_file)
Caleb Lewis
  • 523
  • 6
  • 20
  • Thanks! this worked for my on python on my laptop. I want to test it out next on micropython on a nodeMCU. BTW, I uploade a "txt" file and it showed up as a snippet in the slack channel. I'm going to be uploading large log files so is there a limit to the size of a snipped in slack? – Eradicatore Nov 17 '17 at 19:32
  • well, it doesn't work on micropython as is. "files" parameter isn't there for the post method. Let me know if you have ideas how it might be possible. – Eradicatore Nov 17 '17 at 20:12
  • I answered to your MicroPython forum post: https://forum.micropython.org/viewtopic.php?p=23388#p23388. Feel free to put this into an answer here. – Chris Arndt Nov 21 '17 at 09:52
  • @CalebLewis Is it possible to upload a file as a response to a user? Say I want my chatbot to return a file as a response to my query. – Walter U. Apr 09 '18 at 14:09
  • @Lucif3r - Yes, you can respond to a user. You set the channel property to the user's (or channel's) id. – LeeWallen Feb 20 '19 at 08:36
  • Perfect, solved my issue. I was sending the file in content and needed to send the file in files. – Inaam Ilahi Apr 11 '23 at 06:47
2

Writing this post, to potentially save you all the time I've wasted. I did try to create a new file and upload it to Slack, without actually creating a file (just having it's content). Because of various and not on point errors from the Slack API I wasted few hours to find out that in the end, I had good code from the beginning and simply missed a bot in the channel.

This code can be used also to open an existing file, get it's content, modify and upload it to Slack.

Code:

from io import StringIO # this library will allow us to 
# get a csv content, without actually creating a file.

sio = StringIO()
df.to_csv(sio) # save dataframe to CSV
csv_content = sio.getvalue()
filename = 'some_data.csv'

token=os.environ.get("SLACK_BOT_TOKEN")
url = "https://slack.com/api/files.upload"
request_data = {
    'channels': 'C123456', # somehow required if you want to share the file 
# it will still be uploaded to the Slack servers and you will get the link back
    'content': csv_content, # required
    'filename': filename, # required
    'filetype': 'csv', # helpful :)
    'initial_comment': comment, # optional
    'text': 'File uploaded', # optional
    'title': filename, # optional
    #'token': token, # Don't bother - it won't work. Send a header instead (example below).
}
headers = {
    'Authorization': f"Bearer {token}",
}
response = requests.post(
    url, data=request_data, headers=headers
)

OFFTOPIC - about the docs

I just had a worst experience (probably of this year) with Slack's file.upload documentation. I think that might be useful for you in the future.

Things that were not working in the docs:

  1. token - it cannot be a param of the post request, it must be a header. This was said in one of github bug reports by actual Slack employee.
  2. channel_not_found - I did provide an existing, correct channel ID and got this message. This is somehow OK, because of security reasons (obfuscation), but why there is this error message then: not_in_channel - Authenticated user is not in the channel. After adding bot to the channel everything worked.
  3. Lack of examples for using content param (that's why I am sharing my code with you.
  4. Different codding resulted with different errors regarding form data and no info in the docs helped to understand what might be wrong, what encoding is required in which upload types.

The main issue is they do not version their API, change it and do not update docs, so many statements in the docs are false/outdated.

Mr.TK
  • 1,743
  • 2
  • 17
  • 22
0

Base on the Slack API file.upload documentation What you need to have are:

  • Token : Authentication token bearing required scopes.
  • Channel ID : Channel to upload the file
  • File : File to upload

Here is the sample code. I am using WebClient method in @slack/web-api package to upload it in slack channel.

import { createReadStream } from 'fs';
import { WebClient } from '@slack/web-api';

const token = 'token'
const channelId = 'channelID'
const web = new WebClient(token);

const uploadFileToSlack = async () => {
   await web.files.upload({
     filename: 'fileName',
     file: createReadStream('path/file'),
     channels: channelId,
   });
}
Richard Vergis
  • 1,037
  • 10
  • 20