How to upload files to Slack using Python - python

I want to upload files to Slack using Slack API and Python. I tried to use webhooks but it didn't help me send the file. I am able to send messages only but not files. Is there a way to achieve uploading the file?

Finally, this worked for me.
Steps
Create a Slack app. If you are not the admin you need to request the same.
Add the permissions to read, write.
Get the Authorization token from oauth & permissions
Use the following snippet to upload the file.
url = "https://slack.com/api/files.upload"
headers = {
"Authorization":"Bearer xxxx", ----> this is the token you receive from oauth & permissions. It generally starts with xox
}
payload = { "channels":"channelXYZ"}
file_upload = {
"file":("./hello-world.txt",
open("./hello-world.txt", 'rb'), 'text/plain')
}
response = requests.post(url, headers=headers, files=file_upload, data=payload)

Related

Accomplishing Oauth2.0 authorization with refresh token through Python (Google API service creation)

I'm trying to access Google API services through a headless Linux server using Oauth2. I read through all the answers on this post: How do I authorise an app (web or installed) without user intervention? but none of them showed how to use the refresh token to generate an access token in python. pinnoyyid had a javascript example (https://stackoverflow.com/a/19766913/15713034) that went something like this:
function get_access_token_using_saved_refresh_token() {
// from the oauth playgroundfunction get_access_token_using_saved_refresh_token() {
// from the oauth playground
const refresh_token = "1/0PvMAoF9GaJFqbNsLZQg-f9NXEljQclmRP4Gwfdo_0";
// from the API console
const client_id = "559798723558-amtjh114mvtpiqis80lkl3kdo4gfm5k.apps.googleusercontent.com";
// from the API console
const client_secret = "WnGC6KJ91H40mg6H9r1eF9L";
// from https://developers.google.com/identity/protocols/OAuth2WebServer#offline
const refresh_url = "https://www.googleapis.com/oauth2/v4/token";
let refresh_request = {
body:`grant_type=refresh_token&client_id=${encodeURIComponent(client_id)}&client_secret=${encodeURIComponent(client_secret)}& refresh_token=${encodeURIComponent(refresh_token)}`;,
method: "POST",
headers: new Headers({
'Content-Type': 'application/x-www-form-urlencoded'
})
}
JavaScript isn't really my best language, but I could decipher they were sending a POST request to the google server. So I tried to recreate the request in Python with the requests package:
import requests
result = requests.post("https://www.googleapis.com/oauth2/v4/token", body={'grant_type':'refresh-token', 'client_id':client_id, 'client_secret':client_secret, 'refresh_token': refresh_token}, headers={'Content-Type': 'application/x-www-form-urlencoded'})
And when I look at result it shows it has a 200 status code (success) but when I try to examine the response, there's nothing easy to read and I can't parse the result in JSON to get the access token. The other approach I tried was to spin up a Flask server using Google's suggested code: https://developers.google.com/identity/protocols/oauth2/web-server#python_5 but that doesn't work either because when I try to return the credentials from one of the functions (object that contains the access code) that won't return JSON no matter what. I'd prefer the post request method since it is cleaner and uses less code. Thanks!
In Python, one approach is to use requests-oauthlib to perform the Backend Application Flow. This is useful when you don't have a front-end to redirect someone to, in order to approve fetching a token.
This website (https://community.atlassian.com/t5/Bitbucket-questions/Refresh-Tokens-using-Python-requests/qaq-p/1213162) says solution could be something like this:
import requests
auth = ("<consumer_id>", "<consumer_secret>")
params = {
"grant_type":"refresh_token",
"refresh_token":"<your_refresh_token_here>"
}
url = "https://www.googleapis.com/oauth2/v4/token"
ret = requests.post(url, auth=auth, data=params) #note data=params, not params=params
Since none of the solutions above worked, I had to finally just give up and use a service account.

Amazon Alexa Proactive Events request in Python flask-ask

I am trying to make a request to the Proactive Events API by using the requests module of Python.
However I always receive a response that the scope is invalid.
Can anyone help? What am I doing wrong? My code looks like this:
#ask.launch
def launch():
content_type = "application/x-www-form-urlencoded;charset=utf-8"
client_id = "amzn1.application-oa2-client.6a48XXXXXXX408"
client_secret = "592XXXXxxxxxxx6"
scope = "alexa::proactive_events"
grant_type = "client_credentials"
data = {"grant_type": grant_type, "client_id": client_id, "client_secret": client_secret, "scope": scope}
r = requests.post("https://api.amazon.com/auth/O2/token", data=data, headers={"content-type": content_type})
speech = render_template("welcome")
reprompt = render_template("welcome_reprompt")
return question(speech).reprompt(reprompt)
That is the response I get:
{'error_description': 'The request has an invalid parameter : scope', 'error': 'invalid_scope'}
Since one of the reason you get the invalid scope is that you dont have the events included in your skill manifest I include some steps here. I found quite cumbersome to use the SMAPI to update the skill manifest so instead I used ask cli.
install ask-cli: get authorization code for your amazon acount. In my case the backend is not an AWS lambda function but an external web server
get the skill manifest in json format:
ask api get-skill -s "amzn1.ask.skill.ZZZYYYZZ" --stage development > skill.json
Add the notifications permission and the events elements to the manifest:
{
"name": "alexa::devices:all:notifications:write"
}
and
"events": {
"publications": [
{
"eventName": "AMAZON.AAABBBCC"
}
],
"endpoint": {
"uri": "https://XXXYYYZZ:443/whatevercontext"
}
}
update the manifest:
ask api update-skill -s "amzn1.ask.skill.ZZZYYYZZ" --stage development -f skill.json
enable the notifications in the alexa app for your specific skill
Now you should be able to get the token and next step is to send the notification to the device
Have you tried making the API call via any other method? I just tried that with Postman and it worked for me.
My Python's a bit rusty, but here's the self generated code from Postman for Python. May be this should help?
import http.client
conn = http.client.HTTPConnection("api,amazon,com")
payload = "grant_type=client_credentials&client_id=amzn1.application-oa2-client.whatever-value&client_secret=client-secret&scope=alexa%3A%3Aproactive_events&undefined="
headers = {
'Content-Type': "application/x-www-form-urlencoded"
}
conn.request("POST", "auth,O2,token", payload, headers)
res = conn.getresponse()
data = res.read()
print(data.decode("utf-8"))
As Karthik asked previously have you tried the call via postman? I first suggest trying to via postman before you trying to code it.
If the issue still persists its most likely because you haven't included the necessary permissions required for Proactive Events in your Skill Manifest.
To add the necessary permissions to Skill Manifest you need to use the Skill Management API & ASK Cli.
Follow this section of the documentation and visit the links referenced there to correctly add the required permissions to your skill - https://developer.amazon.com/docs/smapi/proactive-events-api.html#onboard-smapi
Once you have successfully added the events and publications with a skill schema you should be able to successfully generate a token.
Please feel to ask if you want me to elaborate more on the exact steps.
Cheers!
This is what I have tried and it worked:
amazon_token_url = "https://api.amazon.com/auth/O2/token"
headers = {'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'}
body = "grant_type=client_credentials&client_id=amzn1.application-oa2-client.XXXYYYZZ&client_secret=ZZZYYYXXX&scope=alexa::proactive_events"
log.debug("Sending token request with body: %s", body)
resp = requests.post(amazon_token_url, data=body, headers=headers)
resp_json = json.loads(resp.content.decode('utf-8'))
if (resp.status_code < 299) and (resp.status_code > 199):
log.debug("token received from Amazon")
log.debug("Content : %s", resp.content)
token = resp_json["access_token"]
return token

How to upload files to slack using file.upload and requests

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
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)
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:
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.
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.
Lack of examples for using content param (that's why I am sharing my code with you.
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.
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,
});
}

How to send log using logging to slack as message in python and flask?

Give me solution how can i get log directly to slack channel using python and flask.I am able to use logging and save it in log file but i want that info,warning,error,critical,debug logs should directly send to slack channel.
Here is an example of sending a message to a Slack channel via. Python. You must have a Slack web hook configured for your Slack group, which you can then add onto the hook var.
import json, requests
def sendMessageToSlack():
hook = "https://hooks.slack.com/services/<hook goes here>"
headers = {'content-type': 'application/json'}
payload = {"attachments":[
{
"fallback":"",
"pretext":"",
"color":"#fff",
"fields":[
{
"title":"",
"value":"",
"short": False
}
]
}
]
}
r = requests.post(hook, data=json.dumps(payload), headers=headers)
print("Response: " + str(r.status_code) + "," + str(r.reason))
Providing the response returns a 200 code, you should have a message in your channel.
I don't know why you want to utilize flask...
I think you can just use Slack API client together with its Documentation. Basic usage is what you're looking for (coloring etc.).
Using your own solutions isn't recommended - sometimes API changes and you would have to maintain it. Official API client gives you level of abstraction and you don't have to worry about sudden errors.
Posting message (copied from github):
from slackclient import SlackClient
slack_token = os.environ["SLACK_API_TOKEN"]
sc = SlackClient(slack_token)
sc.api_call(
"chat.postMessage",
channel="#python",
text="Hello from Python! :tada:"
)
You can generate test token on this website and try it for yourself.

Cannot post images to Slack channel via web hooks utilizing Python requests

I'm attempting to post an image to a Slack channel utilizing web hooks. This basic setup has allowed me to post text to the channel, but I've been unable to post the image. Here's my code:
def posting():
import requests
import json
url = 'https://webhook'
image = {'media': open('trial.jpg', 'rb')}
r = requests.post(url, files=image)
r.json
When I post the text, a web hook bot appears in the channel and posts it. Do I need some further authentication to post? Or is it a matter of Slack having their own API for uploading and wants me to go through that? Or something something bots don't have rights to post images?
I took a look at some other questions here, but they didn't appear to be using web hooks or bots, so I'm not sure if my issue is something involving those.
You can do this through the Slack API using their files.upload method: https://api.slack.com/methods/files.upload
You will need an API auth token for this to work properly. You can set up a testing token or follow the instructions to register your program to get a long term one: https://api.slack.com/web#basics
Also, 'media' doesn't seem to be the right json key to use for file uploads:
http://docs.python-requests.org/en/master/user/quickstart/#post-a-multipart-encoded-file
Here's an example using requests to send an image to a channel. Use '#username' if you want the image to go to a specific user. I've included the content type and header but it should work without them as well.
This will print the response from Slack.
import requests
def post_image(filename, token, channels):
f = {'file': (filename, open(filename, 'rb'), 'image/png', {'Expires':'0'})}
response = requests.post(url='https://slack.com/api/files.upload', data=
{'token': token, 'channels': channels, 'media': f},
headers={'Accept': 'application/json'}, files=f)
return response.text
print post_image(filename='path/to/file.png', token='xxxxx-xxxxxxxxx-xxxx',
channels ='#general')

Categories