Hooking Up to Slack with Python

The free Slack collaboration environment combined with the Slack Python API  is a great way to automate simple tasks with simple Chat-Bots, automated scripts and background processes that can be written in Python and triggered from Slack on demand.

This blog article leaves the details of what you do with Slack API and just goes through the mechanics of:

Part 1: Posting messages to Slack from a Python client (simple one-way broadcasts to a Slack channel)

Part 2: Two-way interactive communication between a Slack channel and a Python process (this could either be a Python process listening for commands or an interactive chat-bot)

Part 1 – Authentication to Slack and One-Way Posts to Slack

Part 1 will get us authorised to send messages into Slack channels.

1.1 Create a New Application

Go to the https://api.slack.com/apps URL to create a new application.

Give the App a suitable name – pythonbot for instance, and select the Workspace this should be registered with (the app is registered with the whole Slack Workspace, not just a particular channel, so you need permissions to do this).

1.1_create_app

1.2 Review and Set Basic App Information

Scroll down the Basic Information page for the new App.  Note the various App Credentials provided for authenticating to Slack:

1.2_app_credentials_V2

However for the latest Python Slack API client, we don’t need any of the tokens listed on this page.

Scroll down to the bottom of the form, give the App a description and set the background colour in the Display Information section and then select Save Changes.

1.3 Set App Permissions for Workspace and Get OAuth Token

Now we need to get an OAuth Authentication Token for the new App. From the https://api.slack.com/apps page, click on the App you have just created go to the OAuth and Permissions page (select this from the Left Hand Menu bar  as shown in the screenshot below)

  • Add Permission Scopes to allow actions for this API – EG chat:write:bot, channels:read
    • Do this by selecting from the drop-down list of options and then saving the changes.
    • To select all of the above permissions you have to select the permission scopes in two separate steps, saving the changes after each scope operation.
  • Go back to the top of the web-page and select Install App to Workspace.

1.3_OauthAndPermissions

1.4 Authorize the App

When the Install App to Workspace button is selected, it should trigger a re-direct to a page that requests authorisation of the new App to be added to the Workspace.  Select Authorize to move to the page providing the OAuth token.

1.4_Ouath_Confirm_Identity

1.5. Obtain the OAuth Token

After selecting Authorize, the “OAuth & Permissions” page reloads with a generated OAuth Token:

1.5_OAuth_Token

Click copy to copy the token string and paste this into a text-pad somewhere for future use.

1.6 Test Sending Messages to Slack from the Python Slack API

First, install the Slack Client for Python:

pip install slackclient

The Slack Client uses web-sockets, so as long as HTTP is enabled through your firewall, this should work.

Use this simple script to check things are working and we can send messages to Slack:


from slackclient import SlackClient

# Token is stored in code for this test
##!! Don't check this in to GitHub!! ##
slack_token = '_your_token_here_'
sc = SlackClient(slack_token)

sc.api_call(
"chat.postMessage",
channel="",
text="Hello from Python! :tada:",
reply_broadcast=True
)

Use the OAuth Token obtained in section 1.5 that was provided when the new App was registered.

For a destination channel to send messages to, you can specify any public channel in the Workspace that this App is registered in.  (Note – include the preceding “#” when specifying the channel)

If everything is working, the message “Hello from Python!” should appear in the specified channel.

Part 2 – Two Way Communication between Slack and Python Processes

Two-way communication (including receiving input from Slack into a Python process) is more complicated.

However, things are much easier than they were in the past. Previously you had to set up a web-hook process to listen to the Slack Channel and link it to our Python program. Now the Slack Client handles this for us via the Bot Users configuration option.

2.1 Create a Bot User

Go to the main Slack Apps configuration menu at https://api.slack.com/apps , select the name of the App created in the previous steps (EG pythonbot in these examples) and then select the Bot Users configuration menu:

2.1_add_bot_user.png

Select Add a Bot User.  This then provides the option to provide a different Display Name and Default Username than the default (which is just the name of the app).

Either leave the default values as they are or provide alternatives and then click Add Bot User to save these changes and add the bot user.

2.1_add_bot_user_Part2

After Adding the Bot User, make sure you Save Changes.

2.2 Authorise the Bot User

As soon as you save the changes to add the new Bot User, the Slack admin interface should re-direct you to a web-form prompting you to authorise the the Bot User and add it to the Workspace:

2.2_re-confirm_identity

After selecting Authorize, you can then pick up the new OAuth token to authorise this Bot-User with.

2.4 Obtain the Bot User OAuth Token

Obtain the Bot User Oauth Token from the Oauth & Permissions menu.  This is a different token to the one identified previously in Part 1 that allowed messages to posted to public channels.

This token is shorter than the other tokens – at the time of writing in the format:

xoxb-999999999999-999999999999-*************************

Copy the token and paste it into a text-pad session for future reference.

2.4_Slack_Bot_Token

(do not click on Reinstall App)

2.5 Invite the Bot-User to a Channel

Before the Bot can communicate in a channel, it needs to be invited to that channel.  In the example below, the pythonbot Bot User is invited to the channel #test.

2.5_add_bot_to_channel

2.6 Interactive 2-way chat with a Bot – Python echo-bot.

To demonstrate the concept of a Bot that listens to input on a Slack channel and processes what is being said and responds with a message based on the input,  the following “echo-bot” script demonstrates the basic functionality.  This provides all the bare-bones components for developing a more functionally rich chat-bot utility in Python and connecting it to Slack.

First, instead of coding the OAuth tokens into our script (which is very insecure) we should either

  • Export the token value in the shell environment before running our echo-bot utility

Or

  • Create a shell script to conveniently set an environment variable with token value each time we start our echo bot. Make sure the script is not world-readable.

Either way, the syntax in Linux is:

export SLACK_BOT_TOKEN=xoxb-999999999999-999999999999-*************************

(where the actual token values have been obscured in this example).  Simply place the above single line in a shell script such as env.sh and execute as follows

. ./env.sh

to set the SLACK_BOT_TOKEN environment variable so that it can be picked up by the Python script (note the extra “dot”).  We reference the shell environment variable in Python using  os.environ.get

The following script sets up a send-and-receive channel between a Slack channel and a Python client and implements a simple “echo-bot” utility.  This example is copied and modified from here:  https://www.fullstackpython.com/blog/build-first-slack-bot-python.html

import os
import re
import time
from slackclient import SlackClient

# Instantiate an authenticated Slack client
sc = SlackClient(os.environ.get('SLACK_BOT_TOKEN'))
# Bot ID in Slack: ssigned when the bot starts up
bot_id = None

# Constants
READ_DELAY = 0.5 #delay between reading messages
MENTION_REGEX = "^(.*)"

# Get a dictionary of users in this Workspace
def init_users_dict():
    users={}
    users_extract = sc.api_call("users.list")
    for item in users_extract['members']:
        if not item['is_bot']:
            user_data_tuple = ((item['name']
                , item['profile']['display_name']
                , item['real_name']))
            users.update( {item['id'] : user_data_tuple } )
    return users

# Map a users ID to either display name or real name
def map_user(ident, users):
    if users[ident][1]:
        return users[ident][1]
    else:
        return users[ident][2]

# Parse a slack event and extract the message
# and information about channel, fromuser ID, touser ID
def get_msg(slack_events):
    for event in slack_events:
        if event["type"] == "message" and not "subtype" in event:
             touser = None

             message_text = event["text"]
             channel = event["channel"]
             fromuser    =  event["user"]
             matches = re.search(MENTION_REGEX, message_text)
             if matches:
                 touser = matches.group(1)
                 message = matches.group(2)
             else:
                 message = message_text

             return message, channel, fromuser, touser
    else:
        return None, None, None, None
# Send a message on a specific channel ID
def send_msg(message, channel):
    sc.api_call(
        "chat.postMessage",
        channel=channel,
        text=message,
        icon_emoji=':robot_face:'
    )

### MAIN ####
if __name__ == "__main__":
    if sc.rtm_connect(with_team_state=True):
        print("Echo-Bot connected and running")
        users = init_users_dict()

        # Get Bot ID by calling API method `auth.test`
        bot_id = sc.api_call("auth.test")["user_id"]

        while True:
            msg, chnl, fromuser, touser = get_msg(sc.rtm_read())
            if msg:
                if touser == bot_id:
                    ## Incoming Message logic here ##
                    sender_name = map_user(fromuser,users)
                    msg_back = "Hello " + sender_name + ":" + msg
                    send_msg(msg_back, chnl)
            time.sleep(READ_DELAY)
    else:
        print("Connection failed")

When this script is run, it connects to Slack and loops for ever listening to messages for “@pythonbot” and parsing messages and replying to them.

As it stands in this example it just echoes a response back, but this demonstrates the basic framework needed for getting a python script to operate interactively with a Slack channel.

2.6_chatting

Leave a comment