Last week I was working on a Databricks script that needed to produce a Slack message as its final outcome. I lifted some code that used a Slack client that was PIP-installed. Unfortunately, I could not use the package on my cluster. Fortunately, the Slack API is so simple, that you don’t really need a package to post a simple message to a channel. In this blog I’ll show you the simplest way of producing awesome messages in Slack.

This code is not only applicable to Databricks. This works in any Python script.

Setup

I like to manage my settings in a central place, so let’s create some variables to specify the slack token and channel (note: it should start with a #).

slack_token = 'xoxb-my-bot-token'
slack_channel = '#my-channel'
slack_icon_url = 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTuGqps7ZafuzUsViFGIremEL2a3NR0KO0s0RTCMXmzmREJd5m4MA&s'
slack_user_name = 'Double Images Monitor'

I use the same bot for all my messages. To let my users know which application is sending the message, I use a per application icon and user name.

I usually use a single cell for my imports

import requests
import json

Posting to a channel

The Slack API provides a nice chat.postMessage endpoint that we can use. We’ll post a dictionary to the endpoint and a message will appear in our Slack channel:

def post_message_to_slack(text, blocks = None):
    return requests.post('https://slack.com/api/chat.postMessage', {
        'token': slack_token,
        'channel': slack_channel,
        'text': text,
        'icon_url': slack_icon_url,
        'username': slack_user_name,
        'blocks': json.dumps(blocks) if blocks else None
    }).json()	

The only thing we need to do is call the function with a text, for example:

slack_info = 'There are *{}* double images detected for *{}* products. Please check the <https://{}.s3-eu-west-1.amazonaws.com/{}|Double Images Monitor>.'.format(double_images_count,
        products_count, bucket_name, file_name)

post_message_to_slack(slack_info)		

It will look like this:

These text messages will give your users direct information from your scripts and applications.

How about blocks?

I. Love. The. Block. Kit. It allows us to build amazing messages, like this:

Block Kit messages will give you even more control over the format of the message.

You can design block kit messages in the Block Kit Builder. I use python objects to generate the blocks. A block message can be sent by like this:

blocks = [{  
  "type": "section",
  "text": {  
    "type": "mrkdwn",
    "text": ":check: The script has run successfully on the dev."
  }
}]

post_message_to_slack(
   "Text shown in popup.",
   blocks);

How about files?

We can send files through the file.upload API. Unfortunately, it does not support the username and icon_url properties.

def post_file_to_slack(
  text, file_name, file_bytes, file_type=None, title=None
):
    return requests.post(
      'https://slack.com/api/files.upload', 
      {
        'token': slack_token,
        'filename': file_name,
        'channels': slack_channel,
        'filetype': file_type,
        'initial_comment': text,
        'title': title
      },
      files = { 'file': file_bytes }).json()

It makes sending text files really easy:

post_file_to_slack(
    'Check out my text file!',
    'Hello.txt',
    'Hello World!')

Which is displayed like this:

Slack message with a file attached.
Text files will appear in the slack interface.

Also, sending binary data is easy. This code downloads the image URL and passes the bytes on to the function:

import urllib.request

image = "https://images.unsplash.com/photo-1495954484750-af469f2f9be5?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1350&q=80"
response = urllib.request.urlopen(image)
data = response.read()  

post_file_to_slack(
  'Amazing day at the beach. Check out this photo.',
  'DayAtTheBeach.jpg',
  data)

Which looks like this:

Slack message with a photo.
Images are shown in Slack.

Debugging

The Slack API works a little different from other API’s. When a request is invalid, it returns an HTTP 200 with a JSON error message. Because all of our methods return the JSON value, debugging is easy! Just print out the result:

print(post_blocks_to_slack("What is the matter with you!?", {}))

This will tell you all you need to know:

{'ok': False, 'error': 'invalid_blocks_format'}

Conclusion

Slack API. Is. Simple. So… you don’t really need a package to post a message to a Slack channel. Adding messages to your Python scrips and applications will give your end users a richer experience. That’s why I love ChatOps.

Improvements
2019-11-07: Merged sending of blocks and sending of text messages.
2019-11-05: Added .json() returns for all methods to support better debugging. Added debugging section. Added support for blocks.
2019-10-31: Added support for files.