Repository navigation
Expand file tree
/
Copy pathbuild-first-slack-bot-python.html
More file actions
412 lines (369 loc) · 32.3 KB
/
Copy pathbuild-first-slack-bot-python.html
File metadata and controls
412 lines (369 loc) · 32.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="author" content="Matt Makai">
<meta name="description" content="Learn how to build a simple Slack bot in Python, no prior bot experience needed.">
<title>How to Build Your First Slack Bot with Python - Full Stack Python</title>
<link href="/f.css" rel="stylesheet">
<link rel="shortcut icon" href="/img/fsp-fav.png">
</head>
<body>
<div class="container">
<div class="row">
<div class="col-md-12">
<div class="logo-header-section">
<a href="/" style="text-decoration: none; border: none;"><img src="/img/logo-sm.png" class="logo-image" alt="Full Stack Python logo"></a>
<span class="logo-title"><a href="https://www.fullstackpython.com/">Full Stack Python</a></span>
</div>
<div class="sans-font">
<!--<a href="/blog.html" class="submenu-item-first">Blog</a> |
<a href="/books.html" class="submenu-item">Books</a> | -->
<a href="/table-of-contents.html">All topics</a>
<span style="margin:0 12px 0 12px">|</span>
<a href="/blog.html">Blog</a>
<span style="margin:0 12px 0 12px">|</span>
<a href="/email.html">Newsletter</a>
<span style="margin:0 12px 0 12px">|</span>
<a href="https://twitter.com/fullstackpython">@fullstackpython</a>
<span style="margin:0 12px 0 12px">|</span>
<a href="https://www.facebook.com/fullstackpython">Facebook</a>
<span style="margin:0 12px 0 12px">|</span>
<a href="https://github.com/mattmakai/fullstackpython.com">Source</a>
</div> </div>
</div> <div class="row">
<div class="col-md-8">
<h1 style="font-size: 36px;">How to Build Your First Slack Bot with Python</h1>
<div class="post-byline">
Posted by <a href="/about-author.html">Matt Makai</a> on
June 04, 2016.
Last updated
October 27, 2016. </div>
<!--<div width="100%" style="margin: 5px 0 10px 0; height: 250px; overflow: hidden; border-radius: 12px;">
<img src="/source/static/img/160604-simple-python-slack-bot/header.jpg" alt="Slack and Python logos. Copyright their respective owners." width="100%" style="height: 500px; margin: -100px 0 -100px 0;">
</div>-->
<p><a href="/bots.html">Bots</a> are a useful way to interact with chat services such as
<a href="https://slack.com/">Slack</a>. If you have never built a bot before, this
post provides an easy starter tutorial for combining the
<a href="https://api.slack.com/">Slack API</a> with Python to create your first bot.</p>
<p>We will walk through setting up your development environment, obtaining a
Slack API bot token and coding our simple bot in Python.</p>
<h2>Tools We Need</h2>
<p>Our bot, which we will name "StarterBot", requires Python and the Slack API.
To run our Python code we need:</p>
<ul>
<li>Either <a href="/python-2-or-3.html">Python 2 or 3</a></li>
<li><a href="https://pip.pypa.io/en/stable/">pip</a> and
<a href="https://virtualenv.pypa.io/en/stable/">virtualenv</a> to handle Python
<a href="/application-dependencies.html">application dependencies</a></li>
<li><a href="https://slack.com/">Free Slack account</a> with a team on which you have
API access or sign up for the
<a href="http://dev4slack.xoxco.com/">Slack Developer Hangout team</a></li>
<li>Official Python
<a href="https://github.com/slackhq/python-slackclient">slackclient</a> code
library built by the Slack team</li>
<li><a href="https://api.slack.com/tokens">Slack API testing token</a></li>
</ul>
<p>It is also useful to have the <a href="https://api.slack.com/">Slack API docs</a> handy
while you're building this tutorial.</p>
<p>All the code for this tutorial is available open source under the MIT license
in the <a href="https://github.com/mattmakai/slack-starterbot">slack-starterbot</a> public
repository.</p>
<h2>Establishing Our Environment</h2>
<p>We now know what tools we need for our project so let's get our development
environment set up. Go to the terminal (or Command Prompt on Windows) and
change into the directory where you want to store this project. Within
that directory, create a new virtualenv to isolate our application
dependencies from other Python projects.</p>
<div class="highlight"><pre>virtualenv starterbot
</pre></div>
<p>Activate the virtualenv:</p>
<div class="highlight"><pre>source starterbot/bin/activate
</pre></div>
<p>Your prompt should now look like the one in this screenshot.</p>
<p><img src="/source/static/img/160604-simple-python-slack-bot/virtualenv-activate.png" width="100%" class="technical-diagram img-rounded" alt="Command prompt with starterbot's virtualenv activated."></p>
<p>The official slackclient API helper library built by Slack can send and
receive messages from a Slack channel. Install the slackclient library with
the <code>pip</code> command:</p>
<div class="highlight"><pre>pip install slackclient
</pre></div>
<p>When <code>pip</code> is finished you should see output like this and you'll be
back at the prompt.</p>
<p><img src="/source/static/img/160604-simple-python-slack-bot/pip-install-slackclient.png" width="100%" class="technical-diagram img-rounded" alt="Output from using the pip install slackclient command with a virtualenv activated."></p>
<p>We also need to obtain an access token for our Slack team so our bot can
use it to connect to the Slack API.</p>
<h2>Slack Real Time Messaging (RTM) API</h2>
<p>Slack grants programmatic access to their messaging channels via a
<a href="/application-programming-interfaces.html">web API</a>. Go to the
<a href="https://api.slack.com/">Slack web API page</a> and sign up to create your own
Slack team. You can also sign into an existing account where you have
administrative privileges.</p>
<p><img src="/source/static/img/160604-simple-python-slack-bot/sign-in-slack.png" width="100%" class="technical-diagram img-rounded" alt="Use the sign in button on the top right corner of the Slack API page."></p>
<p>After you have signed in go to the
<a href="https://api.slack.com/bot-users">Bot Users page</a>.</p>
<p><img src="/source/static/img/160604-simple-python-slack-bot/custom-bot-users.png" width="100%" class="technical-diagram img-rounded" alt="Custom bot users webpage."></p>
<p>Name your bot "starterbot" then click the “Add bot integration” button.</p>
<p><img src="/source/static/img/160604-simple-python-slack-bot/starterbot.jpg" width="100%" class="technical-diagram img-rounded" alt="Add a bot integration named starterbot."></p>
<p>The page will reload and you will see a newly-generated access token. You
can also change the logo to a custom design. For example, I gave this bot
the Full Stack Python logo.</p>
<p><img src="/source/static/img/160604-simple-python-slack-bot/slack-token.png" width="100%" class="technical-diagram img-rounded" alt="Copy and paste the access token for your new Slack bot."></p>
<p>Click the "Save Integration" button at the bottom of the page. Your bot is
now ready to connect to Slack's API.</p>
<p>A common practice for Python developers is to export secret tokens as
environment variables. Export the Slack token with the name
<code>SLACK_BOT_TOKEN</code>:</p>
<div class="highlight"><pre>export SLACK_BOT_TOKEN='your slack token pasted here'
</pre></div>
<p>Nice, now we are authorized to use the Slack API as a bot.</p>
<p>There is one more piece of information we need to build our bot: our bot's
ID. Next we will write a short script to obtain that ID from the Slack API.</p>
<h2>Obtaining Our Bot’s ID</h2>
<p>It is <em>finally</em> time to write some Python code! We'll get warmed up by coding
a short Python script to obtain StarterBot's ID. The ID varies based on the
Slack team. </p>
<p>We need the ID because it allows our application to determine if messages
parsed from the Slack RTM are directed at StarterBot. Our script also
tests that our <code>SLACK_BOT_TOKEN</code> environment variable is properly set. </p>
<p>Create a new file named <code>print_bot_id.py</code> and fill it with the following
code.</p>
<div class="highlight"><pre><span class="kn">import</span> <span class="nn">os</span>
<span class="kn">from</span> <span class="nn">slackclient</span> <span class="kn">import</span> <span class="n">SlackClient</span>
<span class="n">BOT_NAME</span> <span class="o">=</span> <span class="s">'starterbot'</span>
<span class="n">slack_client</span> <span class="o">=</span> <span class="n">SlackClient</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">environ</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">'SLACK_BOT_TOKEN'</span><span class="p">))</span>
<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">"__main__"</span><span class="p">:</span>
<span class="n">api_call</span> <span class="o">=</span> <span class="n">slack_client</span><span class="o">.</span><span class="n">api_call</span><span class="p">(</span><span class="s">"users.list"</span><span class="p">)</span>
<span class="k">if</span> <span class="n">api_call</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">'ok'</span><span class="p">):</span>
<span class="c"># retrieve all users so we can find our bot</span>
<span class="n">users</span> <span class="o">=</span> <span class="n">api_call</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">'members'</span><span class="p">)</span>
<span class="k">for</span> <span class="n">user</span> <span class="ow">in</span> <span class="n">users</span><span class="p">:</span>
<span class="k">if</span> <span class="s">'name'</span> <span class="ow">in</span> <span class="n">user</span> <span class="ow">and</span> <span class="n">user</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">'name'</span><span class="p">)</span> <span class="o">==</span> <span class="n">BOT_NAME</span><span class="p">:</span>
<span class="k">print</span><span class="p">(</span><span class="s">"Bot ID for '"</span> <span class="o">+</span> <span class="n">user</span><span class="p">[</span><span class="s">'name'</span><span class="p">]</span> <span class="o">+</span> <span class="s">"' is "</span> <span class="o">+</span> <span class="n">user</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">'id'</span><span class="p">))</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">print</span><span class="p">(</span><span class="s">"could not find bot user with the name "</span> <span class="o">+</span> <span class="n">BOT_NAME</span><span class="p">)</span>
</pre></div>
<p>Our code imports the SlackClient and instantiates it with our
<code>SLACK_BOT_TOKEN</code>, which we set as an environment variable. When the
script is executed by the <code>python</code> command we call the Slack API to list
all Slack users and get the ID for the one that matches the name "starterbot".</p>
<p>We only need to run this script once to obtain our bot’s ID.</p>
<div class="highlight"><pre>python print_bot_id.py
</pre></div>
<p>The script prints a single line of output when it is run that provides
us with our bot's ID.</p>
<p><img src="/source/static/img/160604-simple-python-slack-bot/printed-bot-id.png" width="100%" class="technical-diagram img-rounded" alt="Use the Python script to print the Slack bot's ID in your Slack team."></p>
<p>Copy the unique ID that your script prints out. Export the ID as an
environment variable named <code>BOT_ID</code>.</p>
<div class="highlight"><pre>(starterbot)$ export BOT_ID='bot id returned by script'
</pre></div>
<p>The script only needs to be run once to get the bot ID. We can now use
that ID in our Python application that will run StarterBot.</p>
<h2>Coding Our StarterBot</h2>
<p>We've got everything we need to write the StarterBot code. Create a new file
named <code>starterbot.py</code> and include the following code in it.</p>
<div class="highlight"><pre><span class="kn">import</span> <span class="nn">os</span>
<span class="kn">import</span> <span class="nn">time</span>
<span class="kn">from</span> <span class="nn">slackclient</span> <span class="kn">import</span> <span class="n">SlackClient</span>
</pre></div>
<p>The <code>os</code> and <code>SlackClient</code> imports will look familiar because we used them
in the <code>print_bot_id.py</code> program.</p>
<p>With our dependencies imported we can use them to obtain the environment
variable values and then instantiate the Slack client.</p>
<div class="highlight"><pre><span class="cp"># starterbot's ID as an environment variable</span>
<span class="n">BOT_ID</span> <span class="o">=</span> <span class="n">os</span><span class="p">.</span><span class="n">environ</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="s">"BOT_ID"</span><span class="p">)</span>
<span class="cp"># constants</span>
<span class="n">AT_BOT</span> <span class="o">=</span> <span class="s">"<@"</span> <span class="o">+</span> <span class="n">BOT_ID</span> <span class="o">+</span> <span class="s">">"</span>
<span class="n">EXAMPLE_COMMAND</span> <span class="o">=</span> <span class="s">"do"</span>
<span class="cp"># instantiate Slack & Twilio clients</span>
<span class="n">slack_client</span> <span class="o">=</span> <span class="n">SlackClient</span><span class="p">(</span><span class="n">os</span><span class="p">.</span><span class="n">environ</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="err">'</span><span class="n">SLACK_BOT_TOKEN</span><span class="err">'</span><span class="p">))</span>
</pre></div>
<p>The code instantiates the <code>SlackClient</code> client with our <code>SLACK_BOT_TOKEN</code>
exported as an environment variable. </p>
<div class="highlight"><pre>if __name__ == "__main__":
READ_WEBSOCKET_DELAY = 1 # 1 second delay between reading from firehose
if slack_client.rtm_connect():
print("StarterBot connected and running!")
while True:
command, channel = parse_slack_output(slack_client.rtm_read())
if command and channel:
handle_command(command, channel)
time.sleep(READ_WEBSOCKET_DELAY)
else:
print("Connection failed. Invalid Slack token or bot ID?")
</pre></div>
<p>The Slack client connects to the Slack RTM API WebSocket then constantly
loops while parsing messages from the firehose. If any of those messages are
directed at StarterBot, a function named <code>handle_command</code> determines what
to do.</p>
<p>Next add two new functions to parse Slack output and handle commands.</p>
<div class="highlight"><pre><span class="nx">def</span> <span class="nx">handle_command</span><span class="p">(</span><span class="nx">command</span><span class="p">,</span> <span class="nx">channel</span><span class="p">)</span><span class="o">:</span>
<span class="s2">"""</span>
<span class="s2"> Receives commands directed at the bot and determines if they</span>
<span class="s2"> are valid commands. If so, then acts on the commands. If not,</span>
<span class="s2"> returns back what it needs for clarification.</span>
<span class="s2"> """</span>
<span class="nx">response</span> <span class="o">=</span> <span class="s2">"Not sure what you mean. Use the *"</span> <span class="o">+</span> <span class="nx">EXAMPLE_COMMAND</span> <span class="o">+</span> <span class="o">\</span>
<span class="s2">"* command with numbers, delimited by spaces."</span>
<span class="k">if</span> <span class="nx">command</span><span class="p">.</span><span class="nx">startswith</span><span class="p">(</span><span class="nx">EXAMPLE_COMMAND</span><span class="p">)</span><span class="o">:</span>
<span class="nx">response</span> <span class="o">=</span> <span class="s2">"Sure...write some more code then I can do that!"</span>
<span class="nx">slack_client</span><span class="p">.</span><span class="nx">api_call</span><span class="p">(</span><span class="s2">"chat.postMessage"</span><span class="p">,</span> <span class="nx">channel</span><span class="o">=</span><span class="nx">channel</span><span class="p">,</span>
<span class="nx">text</span><span class="o">=</span><span class="nx">response</span><span class="p">,</span> <span class="nx">as_user</span><span class="o">=</span><span class="nx">True</span><span class="p">)</span>
<span class="nx">def</span> <span class="nx">parse_slack_output</span><span class="p">(</span><span class="nx">slack_rtm_output</span><span class="p">)</span><span class="o">:</span>
<span class="s2">"""</span>
<span class="s2"> The Slack Real Time Messaging API is an events firehose.</span>
<span class="s2"> this parsing function returns None unless a message is</span>
<span class="s2"> directed at the Bot, based on its ID.</span>
<span class="s2"> """</span>
<span class="nx">output_list</span> <span class="o">=</span> <span class="nx">slack_rtm_output</span>
<span class="k">if</span> <span class="nx">output_list</span> <span class="nx">and</span> <span class="nx">len</span><span class="p">(</span><span class="nx">output_list</span><span class="p">)</span> <span class="o">></span> <span class="mi">0</span><span class="o">:</span>
<span class="k">for</span> <span class="nx">output</span> <span class="k">in</span> <span class="nx">output_list</span><span class="o">:</span>
<span class="k">if</span> <span class="nx">output</span> <span class="nx">and</span> <span class="s1">'text'</span> <span class="k">in</span> <span class="nx">output</span> <span class="nx">and</span> <span class="nx">AT_BOT</span> <span class="k">in</span> <span class="nx">output</span><span class="cp">[</span><span class="s1">'text'</span><span class="cp">]</span><span class="o">:</span>
<span class="err">#</span> <span class="k">return</span> <span class="nx">text</span> <span class="nx">after</span> <span class="nx">the</span> <span class="err">@</span> <span class="nx">mention</span><span class="p">,</span> <span class="nx">whitespace</span> <span class="nx">removed</span>
<span class="k">return</span> <span class="nx">output</span><span class="cp">[</span><span class="s1">'text'</span><span class="cp">]</span><span class="p">.</span><span class="nx">split</span><span class="p">(</span><span class="nx">AT_BOT</span><span class="p">)</span><span class="cp">[</span><span class="mi">1</span><span class="cp">]</span><span class="p">.</span><span class="nx">strip</span><span class="p">().</span><span class="nx">lower</span><span class="p">(),</span> <span class="o">\</span>
<span class="nx">output</span><span class="cp">[</span><span class="s1">'channel'</span><span class="cp">]</span>
<span class="k">return</span> <span class="nx">None</span><span class="p">,</span> <span class="nx">None</span>
</pre></div>
<p>The <code>parse_slack_output</code> function takes messages from Slack and determines
if they are directed at our StarterBot. Messages that start with a direct
command to our bot ID are then handled by our code - which is currently
just posts a message back in the Slack channel telling the user to write
some more Python code!</p>
<p>Here is how the entire program should look when it's all put together
(you can also
<a href="https://github.com/mattmakai/slack-starterbot/blob/master/starterbot.py">view the file in GitHub</a>):</p>
<div class="highlight"><pre><span class="kn">import</span> <span class="nn">os</span>
<span class="kn">import</span> <span class="nn">time</span>
<span class="kn">from</span> <span class="nn">slackclient</span> <span class="kn">import</span> <span class="n">SlackClient</span>
<span class="c"># starterbot's ID as an environment variable</span>
<span class="n">BOT_ID</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">environ</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">"BOT_ID"</span><span class="p">)</span>
<span class="c"># constants</span>
<span class="n">AT_BOT</span> <span class="o">=</span> <span class="s">"<@"</span> <span class="o">+</span> <span class="n">BOT_ID</span> <span class="o">+</span> <span class="s">">"</span>
<span class="n">EXAMPLE_COMMAND</span> <span class="o">=</span> <span class="s">"do"</span>
<span class="c"># instantiate Slack & Twilio clients</span>
<span class="n">slack_client</span> <span class="o">=</span> <span class="n">SlackClient</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">environ</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">'SLACK_BOT_TOKEN'</span><span class="p">))</span>
<span class="k">def</span> <span class="nf">handle_command</span><span class="p">(</span><span class="n">command</span><span class="p">,</span> <span class="n">channel</span><span class="p">):</span>
<span class="sd">"""</span>
<span class="sd"> Receives commands directed at the bot and determines if they</span>
<span class="sd"> are valid commands. If so, then acts on the commands. If not,</span>
<span class="sd"> returns back what it needs for clarification.</span>
<span class="sd"> """</span>
<span class="n">response</span> <span class="o">=</span> <span class="s">"Not sure what you mean. Use the *"</span> <span class="o">+</span> <span class="n">EXAMPLE_COMMAND</span> <span class="o">+</span> \
<span class="s">"* command with numbers, delimited by spaces."</span>
<span class="k">if</span> <span class="n">command</span><span class="o">.</span><span class="n">startswith</span><span class="p">(</span><span class="n">EXAMPLE_COMMAND</span><span class="p">):</span>
<span class="n">response</span> <span class="o">=</span> <span class="s">"Sure...write some more code then I can do that!"</span>
<span class="n">slack_client</span><span class="o">.</span><span class="n">api_call</span><span class="p">(</span><span class="s">"chat.postMessage"</span><span class="p">,</span> <span class="n">channel</span><span class="o">=</span><span class="n">channel</span><span class="p">,</span>
<span class="n">text</span><span class="o">=</span><span class="n">response</span><span class="p">,</span> <span class="n">as_user</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">parse_slack_output</span><span class="p">(</span><span class="n">slack_rtm_output</span><span class="p">):</span>
<span class="sd">"""</span>
<span class="sd"> The Slack Real Time Messaging API is an events firehose.</span>
<span class="sd"> this parsing function returns None unless a message is</span>
<span class="sd"> directed at the Bot, based on its ID.</span>
<span class="sd"> """</span>
<span class="n">output_list</span> <span class="o">=</span> <span class="n">slack_rtm_output</span>
<span class="k">if</span> <span class="n">output_list</span> <span class="ow">and</span> <span class="nb">len</span><span class="p">(</span><span class="n">output_list</span><span class="p">)</span> <span class="o">></span> <span class="mi">0</span><span class="p">:</span>
<span class="k">for</span> <span class="n">output</span> <span class="ow">in</span> <span class="n">output_list</span><span class="p">:</span>
<span class="k">if</span> <span class="n">output</span> <span class="ow">and</span> <span class="s">'text'</span> <span class="ow">in</span> <span class="n">output</span> <span class="ow">and</span> <span class="n">AT_BOT</span> <span class="ow">in</span> <span class="n">output</span><span class="p">[</span><span class="s">'text'</span><span class="p">]:</span>
<span class="c"># return text after the @ mention, whitespace removed</span>
<span class="k">return</span> <span class="n">output</span><span class="p">[</span><span class="s">'text'</span><span class="p">]</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="n">AT_BOT</span><span class="p">)[</span><span class="mi">1</span><span class="p">]</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span><span class="o">.</span><span class="n">lower</span><span class="p">(),</span> \
<span class="n">output</span><span class="p">[</span><span class="s">'channel'</span><span class="p">]</span>
<span class="k">return</span> <span class="bp">None</span><span class="p">,</span> <span class="bp">None</span>
<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">"__main__"</span><span class="p">:</span>
<span class="n">READ_WEBSOCKET_DELAY</span> <span class="o">=</span> <span class="mi">1</span> <span class="c"># 1 second delay between reading from firehose</span>
<span class="k">if</span> <span class="n">slack_client</span><span class="o">.</span><span class="n">rtm_connect</span><span class="p">():</span>
<span class="k">print</span><span class="p">(</span><span class="s">"StarterBot connected and running!"</span><span class="p">)</span>
<span class="k">while</span> <span class="bp">True</span><span class="p">:</span>
<span class="n">command</span><span class="p">,</span> <span class="n">channel</span> <span class="o">=</span> <span class="n">parse_slack_output</span><span class="p">(</span><span class="n">slack_client</span><span class="o">.</span><span class="n">rtm_read</span><span class="p">())</span>
<span class="k">if</span> <span class="n">command</span> <span class="ow">and</span> <span class="n">channel</span><span class="p">:</span>
<span class="n">handle_command</span><span class="p">(</span><span class="n">command</span><span class="p">,</span> <span class="n">channel</span><span class="p">)</span>
<span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="n">READ_WEBSOCKET_DELAY</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">print</span><span class="p">(</span><span class="s">"Connection failed. Invalid Slack token or bot ID?"</span><span class="p">)</span>
</pre></div>
<p>Now that all of our code is in place we can run our StarterBot on the
command line with the <code>python starterbot.py</code> command.</p>
<p><img src="/source/static/img/160604-simple-python-slack-bot/starterbot-running.png" width="100%" class="technical-diagram img-rounded" alt="Console output when the StarterBot is running and connected to the API."></p>
<p>In Slack, create a new channel and invite StarterBot or invite it to an
existing channel.</p>
<p><img src="/source/static/img/160604-simple-python-slack-bot/create-channel.png" width="100%" class="technical-diagram img-rounded" alt="In the Slack user interface create a new channel and invite StarterBot."></p>
<p>Now start giving StarterBot commands in your channel.</p>
<p><img src="/source/static/img/160604-simple-python-slack-bot/working-starterbot.png" width="100%" class="technical-diagram img-rounded" alt="Give StarterBot commands in your Slack channel."></p>
<p>As it is currently written above in this tutorial, the
line <code>AT_BOT = "<@" + BOT_ID + ">"</code> does not require a colon after the
"@starter" (or whatever you named your particular bot) mention. Previous
versions of this tutorial did have a colon because Slack clients would
auto-insert the ":" but that is no longer the case.</p>
<h2>Wrapping Up</h2>
<p>Alright, now you've got a simple StarterBot with a bunch of places in the
code you can add whatever features you want to build. </p>
<p>There is a whole lot more that could be done using the Slack RTM API and Python.
Check out these posts to learn what you could do:</p>
<ul>
<li>Attach a persistent <a href="/databases.html">relational database</a> or
<a href="/no-sql-datastore.html">NoSQL back-end</a> such as
<a href="/postgresql.html">PostgreSQL</a>, <a href="/mysql.html">MySQL</a> or <a href="/sqlite.html">SQLite</a>
to save and retrieve user data</li>
<li>Add another channel to interact with the bot
<a href="https://www.twilio.com/blog/2016/05/build-sms-slack-bot-python.html">via SMS</a>
or
<a href="https://www.twilio.com/blog/2016/05/add-phone-calling-slack-python.html">phone calls</a></li>
<li><a href="/api-integration.html">Integrate other web APIs</a> such as
<a href="https://developer.github.com/v3/">GitHub</a>,
<a href="https://www.twilio.com/docs">Twilio</a> or <a href="https://docs.api.ai/">api.ai</a></li>
</ul>
<p>Questions? Contact me via Twitter
<a href="https://twitter.com/fullstackpython">@fullstackpython</a>
or <a href="https://twitter.com/mattmakai">@mattmakai</a>. I'm also on GitHub with
the username <a href="https://github.com/mattmakai">mattmakai</a>.</p>
<p>Something wrong with this post? Fork
<a href="https://github.com/mattmakai/fullstackpython.com/blob/gh-pages/source/content/posts/160604-build-first-slack-bot-python.markdown">this page's source on GitHub</a>.</p>
<hr>
<div id="mc_embed_signup">
<form action="//mattmakai.us2.list-manage.com/subscribe/post?u=b7e774f0c4f05dcebbfee183d&id=b22335388d" method="post" id="mc-embedded-subscribe-form" name="mc-embedded-subscribe-form" class="validate" target="_blank" novalidate>
<div id="mc_embed_signup_scroll">
<h4>Sign up here to receive a monthly email with major updates to this site, tutorials and discount codes for Python books.</h4>
<div class="row">
<div class="col-md-9">
<input type="email" value="" name="EMAIL" class="email form-control" id="mce-EMAIL" placeholder="email address" required>
<div style="position: absolute; left: -5000px;"><input type="text" name="b_b7e774f0c4f05dcebbfee183d_b22335388d" tabindex="-1" value=""></div>
</div>
<div class="col-md-3">
<div class="clear"><input type="submit" value="Subscribe" name="subscribe" id="mc-embedded-subscribe" class="btn btn-success" style="font-family: 'Helvetica Neue';"></div>
</div>
</div>
</div>
</form>
</div>
</div>
<div class="col-md-offset-1 col-md-3">
<div class="panel" id="sidebar">
<div class="panel-heading">
<h3 class="panel-head">Learn more about these concepts</h3>
</div>
<img src="/source/static/img/160604-simple-python-slack-bot/header.jpg" alt="Slack and Python logos. Copyright their respective owners." width="100%">
<div class="list-group">
<a href="/learning-programming.html" class="list-group-item smaller-item">Learning Python</a>
<a href="/bots.html" class="list-group-item smaller-item">Bots</a> <a href="/table-of-contents.html" class="list-group-item smaller-item" style='background-color:#444; color: #fff;'>...or <span style="border-bottom: 1px dotted;">view all topics</span>.</a>
</div>
</div> </div>
</div>
<hr/>
</div>
<div class="container">
<div class="sans-font footer">
<a href="https://github.com/mattmakai" class="underline">Matt Makai</a> 2016
</div>
</div><script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-19910497-7', 'auto');
ga('send', 'pageview');
</script><script type='text/javascript'>
var trackOutboundLink = function(url) { ga('send', 'event', 'outbound', 'click', url, {'hitCallback': function () { document.location = url; } }); }
</script>
</body>
</html>