How to make a Chatbot with Python (for Slack)
A Step by Step Tutorial
One amazing thing about human beings is that you would talk to them, and they would answer you back… well, most of the time.
Now, what if you could make a bot that can answer back as well. What if you could add it to your plant or dishwasher. Wouldn’t that be awesome!
While this project is not about building an amazingly intelligent chatbot it will help you setup the basic foundations using Python and Slack. So, whether it is because you want to make your own virtual assistant, change the world or if you just do not have friends and want to talk to a bot, this post is the right one for you! Enjoy :)
The Basics — How it works
“The basics are the basics, and you cannot beat the basics” — Charles Poliquin
This section will introduce the basics of how the bot will work. So, without further ado, here’s what we’re going to build:
Our current chatbot, whose name from now on will be: “Valet DeMachin”, will be a ridiculously dumb bad pun assistant. But for now we will make the simplest version possible.
Valet will be able to do the following:
- Answer with something like ‘Hello @derp !’ when greeted by user @derp.
- Answer with a farewell when given a farewell.
- Do nothing otherwise.
So for now, to get started our chatbot will be super simple so we can understand how the Slack Event API works before starting to do any more advanced stuff.
Later versions can include giving recipes on what’s for dinner, smarter answers using machine learning and ridiculously bad puns and pick up lines.
(If you want to hear about the smarter updated version join the mailing list here and I promise to keep you updated ;) )
What you need first
- A computer, if you haven’t figured that out already.
- Have an account on Slack. Either be already on a team in Slack or create a new team.
- We’re going to use Python3.5.1, you can use other versions of Python.
- Python Slack Client API slackclient. You can install using pip (which you can install from here) with the command :
$> pip install slackclient
Ready, set, go!
Practice Time whenever you see this sign, there will be some practice to do ;) So try not to skip until you’re done. And if you experience any difficulties, just let me know by leaving a comment and I’ll respond as fast as possible :D
Making the bot on Slack
Go to the Slack API Bot User page.
Scroll down to Custom Bot Users and click on creating a new bot user.
Enter the Username for the bot, then click on Add bot integration.
Now you should have an API Token that would appear on the next page and looks something like this:
xoxo...<some random characters here>...l0V3
Optionally you can add a picture and a real name for your slackbot.
You should already be able to see it on Slack.
let’s see how we can get it running on Python.
Checking that everything works correctly
Let’s start by exporting the slack name and the slack token of our bot instead of hardcoding them (sticking with the good habits, right :P )
We can do this by either typing this into the command line, in the working directory of the valet:
$> export VALET_SLACK_TOKEN='YOUR AUTH TOKEN GOES HERE'
$> export VALET_SLACK_NAME='valet'
The way we’re going to do it is by adding a source.sh file with the following:
export VALET_SLACK_TOKEN='YOUR AUTH TOKEN GOES HERE'
export VALET_SLACK_NAME='valet'
and then sourcing the file from the command line by using:
$> sh source.sh
This way we can reuse it later (there are better ways of doing this but let’s keep it simple for now ;p )
Now let’s check that the authentication token works properly. We import the environment variables and check if we can call the slack client successfully, we make a discover.py file that contains the following:
import os, slackclient
VALET_SLACK_NAME = os.environ.get('VALET_SLACK_NAME')
VALET_SLACK_TOKEN = os.environ.get('VALET_SLACK_TOKEN')# initialize slack client
valet_slack_client = slackclient.SlackClient(VALET_SLACK_TOKEN)# check if everything is alright
print(VALET_SLACK_NAME)
print(VALET_SLACK_TOKEN)
is_ok = valet_slack_client.api_call("users.list").get('ok')
print(is_ok)
Practice Time check if the print method prints the correct variables for both your bot name and token. If these do not work properly, chances are you had a problem sourcing the environment variables. If it doesn’t seem to work leave me a comment here. Now let’s move on to the good stuff…
We need to find the bot id. Just like any other user, your chatbot has a user id. To find it we add the following lines to our discover.py file:
# find the id of our slack bot
if(is_ok):
for user in valet_slack_client.api_call("users.list").get('members'):
if user.get('name') == VALET_SLACK_NAME:
print(user.get('id'))
What this does is it goes through the list of users and prints out the id of our lovely Valet De Machin.
We find out that our chatbot’s id is something like :
U345F2UP9
We change the source.sh file to include the id of the chatbot:
export VALET_SLACK_TOKEN='YOUR_AUTH_TOKEN_GOES_HERE'
export VALET_SLACK_NAME='valet'
export VALET_SLACK_ID='U345F2UP9'
and, of course we need to source it again
sh source.sh
Starting small
What would this look like if it were easy? — Tim Ferriss
Now let’s make a very simple bot that will run and respond with hello.
We create a new python file valet.py containing the following:
import os, slackclient, time
import random
# delay in seconds before checking for new events
SOCKET_DELAY = 1# slackbot environment variables
VALET_SLACK_NAME = os.environ.get('VALET_SLACK_NAME')
VALET_SLACK_TOKEN = os.environ.get('VALET_SLACK_TOKEN')
VALET_SLACK_ID = os.environ.get('VALET_SLACK_ID')valet_slack_client = slackclient.SlackClient(VALET_SLACK_TOKEN)def is_for_me(event):
# TODO Implement later
return Truedef handle_message(message, user, channel):
# TODO Implement later
post_message(message='Hello', channel=channel)def post_message(message, channel):
valet_slack_client.api_call('chat.postMessage', channel=channel,
text=message, as_user=True)def run():
if valet_slack_client.rtm_connect():
print('[.] Valet de Machin is ON...')
while True:
event_list = valet_slack_client.rtm_read()
if len(event_list) > 0:
for event in event_list:
print(event)
if is_for_me(event):
handle_message(message=event.get('text'), user=event.get('user'), channel=event.get('channel'))
time.sleep(SOCKET_DELAY)
else:
print('[!] Connection to Slack failed.')
if __name__=='__main__':
run()
Notice that for now we have kept the is_for_me and handle_message as simple as we can.
We want is_for_me to understand whether the message is dedicated to our chatbot. To make it simple we consider that it can be either of the following cases:
- The message is sent on a private chat.
- The message is sent out on a group channel and the valet is mentioned.
Since I couldn’t find a better way to know if a channel is private, I did so by implementing a function is_private that checks if the event starts with a ‘D’ since it seems like that is what makes the difference between a private channel and others (if you find a better way, please let me know I’d be more than happy to check it out, here are the event api docs by the way: https://api.slack.com/events/)
def is_private(event):
"""Checks if private slack channel"""
return event.get('channel').startswith('D')
Practice time Try to implement the is_for_me and handle_message function, on your own first, by checking the docs for the message events at https://api.slack.com/events/message
Note that you can find the source code on the following github repository
Here’s a way to implement the previous functions:
we first add this function that enables us to get how the chatbot is mentioned:
# how the bot is mentioned on slack
def get_mention(user):
return '<@{user}>'.format(user=user)
valet_slack_mention = get_mention(VALET_SLACK_ID)
We then finish is_for_me as follows:
def is_for_me(event):
"""Know if the message is dedicated to me"""
# check if not my own event
type = event.get('type')
if type and type == 'message' and not(event.get('user')==VALET_SLACK_ID):
# in case it is a private message return true
if is_private(event):
return True
# in case it is not a private message check mention
text = event.get('text')
channel = event.get('channel')
if valet_slack_mention in text.strip().split():
return True
To handle a message we also use get_mention to mention the user we’re talking to when we say hi. We want to say Hi only when the user is saying Hi or Hello or something similar. And we want to say bye only when the user is giving their farewells to our chatbot.
This means that we first need to add a function that will know if the user is saying hi and a function to know if the user is saying goodbye.
We implement these two very simple functions for now by just saying that if a text contains something like hello then it is a greeting (for more interesting stuff with nltk tune in by subscribing to my mailing list at http://ismail.land)
def is_hi(message):
tokens = [word.lower() for word in message.strip().split()]
return any(g in tokens
for g in ['hello', 'bonjour', 'hey', 'hi', 'sup', 'morning', 'hola', 'ohai', 'yo'])
def is_bye(message):
tokens = [word.lower() for word in message.strip().split()]
return any(g in tokens
for g in ['bye', 'goodbye', 'revoir', 'adios', 'later', 'cya'])
And we also implement the following methods to answer back by either hi or goodbye:
def say_hi(user_mention):
"""Say Hi to a user by formatting their mention"""
response_template = random.choice(['Sup, {mention}...',
'Yo!',
'Hola {mention}',
'Bonjour!'])
return response_template.format(mention=user_mention)
def say_bye(user_mention):
"""Say Goodbye to a user"""
response_template = random.choice(['see you later, alligator...',
'adios amigo',
'Bye {mention}!',
'Au revoir!'])
return response_template.format(mention=user_mention)
With all of this, all that is left now is for us to finally implement our handle_message function:
def handle_message(message, user, channel):
if is_hi(message):
user_mention = get_mention(user)
post_message(message=say_hi(user_mention), channel=channel)
elif is_bye(message):
user_mention = get_mention(user)
post_message(message=say_bye(user_mention), channel=channel)
Finally, we run our valet.py file from the command line using
$> python valet.py
Note : If this does not work make sure you have sourced the environment variables. If not just run sh source.sh again :)
And now we can finally get on slack and talk to our chatbot.
Either from a private chat:
Or from a public channel:
Thanks for reading! :) If you enjoyed it, hit that heart button below. Would mean a lot to me and it helps other people see the story.
Of course, we later want our chatbot to be able to do more fun stuff and be used in other ways. To tune in for a part 2 on how to make your chatbot smarter and how to connect it to facebook you can join me over here on my mailing list and I promise to keep you updated!
By the way, Python3.6 is finally out, I got some time to have my hands on it, here’s a summary of the features that I’m most excited about.