├── .github └── CODE_OF_CONDUCT.md ├── .gitignore ├── LICENSE ├── README.md └── docs ├── Chapter-1.md ├── Chapter-2.md ├── Chapter-3.md ├── Chapter-4.md └── Chapter-5.md /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | ## Introduction 4 | 5 | Diversity and inclusion make our community strong. We encourage participation from the most varied and diverse backgrounds possible and want to be very clear about where we stand. 6 | 7 | Our goal is to maintain a safe, helpful and friendly community for everyone, regardless of experience, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, religion, nationality, or other defining characteristic. 8 | 9 | This code and related procedures also apply to unacceptable behavior occurring outside the scope of community activities, in all community venues (online and in-person) as well as in all one-on-one communications, and anywhere such behavior has the potential to adversely affect the safety and well-being of community members. 10 | 11 | For more information on our code of conduct, please visit [https://slackhq.github.io/code-of-conduct](https://slackhq.github.io/code-of-conduct) -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | env/ 2 | *.pyc 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Shannon Burns 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Build This Bot 2 | 3 | I want you to build a bot. This bot. Build this bot. 4 | 5 | ## You want me to _what?_ 6 | 7 | Have you ever wanted an automaton to call your own? The intent of this project is to make that dream a reality by guiding you through the ins and outs of bot building on Slack. 8 | 9 | ## But _why?_ 10 | 11 | Once upon a time in a galaxy far far away, a team called Tiny Speck built a tool to communicate with each other as they worked. They felt frustrated by the amount of mindless repetitive tasks that slowed down their communication and productivity. As avid consumers of Sci-Fi and generally against things that made them frustrated, the Tiny Speck team built a special user in their messaging app: a digital user or a bot. 12 | Instead of forcing the intern to do these mindless tasks, they delegated these responsibilities to their new bot user and integrated the other apps they used into their conversations. 13 | 14 | Eventually this communication tool became known as Slack, and their digital user, Slackbot. 15 | 16 | Now you too can build a Slack Bot and free up yourself, your fellow coworkers, and even the customers using your products and services, so we can all get back to what we really should be doing; _~~sleep~~_ **work!** 17 | 18 | ## Let's get started :tada: 19 | 20 | First you'll want to clone this project locally. This project is structured with different branches for different stages of the workshop, so you can add your own code and follow along. If you happen to run into a bug or get stuck and we're moving on you can check out the next branch and you won't be left behind. :fist: 21 | 22 | At each step, feel free to ask questions in the _#help_ channel, or ask your neighbor if they can help! :raised_hands: 23 | 24 | To get started, check out the first branch 25 | 26 | ```bash 27 | git checkout chapter-1 28 | ``` 29 | 30 | If you'd like to follow along without any of the code, all of the documentation for each chapter is located in the `docs` folder in the `master` branch. 31 | 32 | * **[Chapter 1: Setting Things Up](docs/Chapter-1.md)** :point_left: 33 | * [Chapter 2: OMG OAuth](docs/Chapter-2.md) 34 | * [Chapter 3: Handle Hello with Class](docs/Chapter-3.md) 35 | * [Chapter 4: Message Buttons Magic](docs/Chapter-4.md) 36 | * [Chapter 5: ABCs of NLP](docs/Chapter-5.md) 37 | 38 | ## Further Reading and Getting Help 39 | 40 | ### Documentation 41 | 42 | ##### Slack Documentation 43 | 44 | * [Getting started with Slack apps](https://api.slack.com/slack-apps?utm_source=events&utm_campaign=build-bot-workshop&utm_medium=workshop) 45 | * [Slack Events API documentation](https://api.slack.com/events?utm_source=events&utm_campaign=build-bot-workshop&utm_medium=workshop) 46 | * [Slack Web API documentation](https://api.slack.com/web?utm_source=events&utm_campaign=build-bot-workshop&utm_medium=workshop) 47 | 48 | ##### Documentation for Tools 49 | 50 | * [virtualenv](https://virtualenv.pypa.io/en/latest/userguide/) 51 | * [flask](http://flask.pocoo.org/) 52 | * [python-slackclient](http://python-slackclient.readthedocs.io/en/latest/) 53 | * [Slack Events Adapter](https://github.com/slackapi/python-slack-events-api) 54 | * [ngrok](https://ngrok.com/docs) 55 | * [postman](https://www.getpostman.com/docs/) 56 | 57 | ### Where to Find Help 58 | 59 | Wondering what to do if you can't get this dang tutorial to work for you? 60 | The Slack Developer community is an awesome place to get help when you're confused 61 | or stuck. We have an excellent 'search first' culture and Slack is committed to 62 | improving our tutorials and documentation based on your feedback. If you've 63 | checked the [Slack API documentation](https://api.slack.com/?utm_source=events&utm_campaign=build-bot-workshop&utm_medium=workshop), reached the end 64 | of your google patience and found [StackOverflow](http://stackoverflow.com/questions/tagged/slack-api) 65 | to be unhelpful, try asking for help in the [Bot Developer Hangout](http://dev4slack.xoxco.com/) 66 | Slack team. 67 | 68 | ### Feedback 69 | 70 | I'd love to improve this project, so if you've got some ideas :bulb:, feedback 71 | :raising_hand: or praise :love_letter: please file an issue, submit a PR or 72 | reach out to me through [Github](https://github.com/karishay) or on 73 | [Twitter](https://twitter.com/karishannon)! 74 | 75 | If you want to keep up with Slack platform updates chat with us on [Twitter](https://twitter.com/slackapi). 76 | -------------------------------------------------------------------------------- /docs/Chapter-1.md: -------------------------------------------------------------------------------- 1 | # Chapter 1: Setting Things Up 2 | 3 | Before we get to the core of bot building, we need to get set up. First, we'll add everything you need for creating a local web server where our bot will live and an https ngrok tunnel so that our bot can connect to Slack. After we've set up our development environment, we'll set up a new Slack App and bot user and then we'll connect Slack to our local environment. Once our environment is set up, we'll get our Flask application server running. 4 | 5 | ## Setting up Your Development Environment 6 | 7 | Let's jump right in! :raised_hands: 8 | 9 | This example uses [Python](https://www.python.org/downloads/), specifically 10 | version 2.7 so you'll need to make sure you are using the correct version of 11 | Python. If you are using a Mac or Unix-like OS, it is likely that you will already 12 | have this version of Python installed. If you're using windows, you'll need to download and run the Python installer. We'll also use a number of python 13 | packages you can install through [pip.](https://pip.pypa.io/en/stable/installing/) 14 | 15 | ###### Here's a list of what we'll need: 16 | 17 | - **[Python](https://www.python.org/downloads/)**, the programming language we're 18 | going to use. 19 | - **[Pip](https://pip.pypa.io/en/stable/installing/)**, the Python package manager 20 | we'll use for installing packages we need. 21 | - **[Virtualenv](https://virtualenv.pypa.io/en/latest/installation/)** or another 22 | tool to manage a virtual environment 23 | - **[Ngrok](https://ngrok.com/)**, an easy to use tunneling tool that supports HTTPS, 24 | which we'll use to connect our app to Slack over _teh interwebz._ 25 | 26 | After you’ve installed Python, pip, ngrok and virtualenv you’ll need to generate and activate a new virtualenv. Once your virtualenv is turned on you can install all the additional dependent libraries using pip and the `requirements.txt` file in this project, including [Flask](http://flask.pocoo.org/), a web development microframework for Python, [python-slackclient](http://python-slackclient.readthedocs.io/en/latest/), a Slack client for Python and the [Slack Events Adapter for Python](https://github.com/slackapi/python-slack-events-api). :snake: 27 | 28 | First, you'll want to create a virtual environment to keep the dependencies for this project isolated from any other project you may be working on. You'll need to open a terminal or command prompt to enter these and the following commands. 29 | 30 | Since we're using virtualenv you can run the following commands from the root of your 31 | project directory: 32 | 33 | ```bash 34 | virtualenv env 35 | ``` 36 | 37 | Then activate your new virtual environment: 38 | 39 | ```bash 40 | source env/bin/activate 41 | ``` 42 | 43 | After that, you can install all the Python packages this project will need with 44 | this command: 45 | 46 | ```bash 47 | pip install -r requirements.txt 48 | ``` 49 | 50 | ## Setting up Your Slack App 51 | 52 | Now that we've got our local environment set up we'll need to create a new Slack App. :tada: 53 | 54 | ### Creating a New Slack App on [api.slack.com](https://api.slack.com/apps?utm_source=events&utm_campaign=build-bot-workshop&utm_medium=workshop) 55 | 56 | In your browser, on [api.slack.com/apps](https://api.slack.com/apps?utm_source=events&utm_campaign=build-bot-workshop&utm_medium=workshop) you'll find 57 | a green button labeled [Create New App](https://api.slack.com/apps/new?utm_source=events&utm_campaign=build-bot-workshop&utm_medium=workshop) on the 58 | top right of the page. 59 | 60 |  61 | 62 | _Push the tempting button_ :point_right: :white_check_mark: 63 | 64 | You'll be directed to your shiny new app's **Basic Information** page. We'll come back to you soon, _basic information_. 65 | 66 | ### Adding a Bot User 67 | 68 | But first, let's get ourselves a shiny new **Bot User** so our app can communicate on Slack. On the left side navigation you'll find the **Bot Users** tab where you can create a new bot user for your app. 69 | 70 |  71 | 72 |  73 | 74 | Once you've got your fancy new automaton, we have it subscribe to events in Slack! 75 | 76 | ### Subscribe to Events 77 | 78 | By using Slack's [Events API](https://api.slack.com/events-api?utm_source=events&utm_campaign=build-bot-workshop&utm_medium=workshop) we can ask Slack to send us a JSON payload of information when something particular happens inside of Slack. If we want our bot respond when a user says hello to our bot in a DM, we can have our bot subscribe to `message.im` and when a user posts a message, Slack will send the information about the message event to the URL we specify. If the message matches our criteria we can choose to respond to it with an additional step. Unneeded events are discarded. 79 | 80 | On the left navigation bar of your app's settings page you'll find **Event Subscriptions**. 81 | To start off right, go ahead and subscribe your bot to `message.im` events under the **Bot Events** section of the page. 82 | 83 |  84 | 85 | 86 | After you've subscribed to all the events your app will need, make sure to **Save Changes**. 87 | 88 |  89 | 90 | 91 | ### App Credentials 92 | 93 | Let's revisit that tempting **Basic Information** page. Here you'll find your app's **Client ID**, **Client Secret** and **Verification Token** under the _App Credentials_ section. 94 | 95 |  96 | 97 | The Client ID and Client Secret are used to validate your app's authenticity during the OAuth negotiation process. The Verification Token is used to verify requests sent by Slack and received by your server. 98 | 99 | Just like you wouldn't graffiti your email username and password at the bus stop, it's important to prevent your _App Credentials_ from becoming part of a public repository. To protect your app's secrets, this project exports these secrets to your local environment. 100 | 101 | If you're using Bash or Zsh and your virtual environment is activated, you can export your app's secrets like this: 102 | 103 | :warning: _Remember to have your virtualenv activated before exporting environment variables._ 104 | 105 | ```bash 106 | export CLIENT_ID='XXXXXXXXXXX.xxxxxxxxxx' 107 | export CLIENT_SECRET='xxXXxxXXXXXxxxxXXX' 108 | export VERIFICATION_TOKEN='xxxXXXxxXXxxX' 109 | ``` 110 | 111 | If you're using Windows, you can export your app's secret's like this: 112 | 113 | ```dos 114 | set CLIENT_ID='XXXXXXXXXXX.xxxxxxxxxx' 115 | set CLIENT_SECRET='xxXXxxXXXXXxxxxXXX' 116 | set VERIFICATION_TOKEN='xxxXXXxxXXxxX' 117 | ``` 118 | 119 | Our app will grab these secrets from our environment. 120 | 121 | ## Let's Make an [app.py](app.py) 122 | 123 | In this project directory, you'll find a file called [app.py](app.py). Slack will be delivering events to your application securely, so your application server will need to be able to receive incoming HTTPS traffic. In [app.py](app.py) we'll use the [Slack Events Adapter](https://github.com/slackapi/python-slack-events-api) to create a Flask server to handle all incoming events from Slack. But first, you'll need a web page where people can install your bot onto their teams! 124 | 125 | When you open [app.py](app.py) you'll see that we've created an instance of _SlackEventAdapter_ called `events_adapter` and added a couple of routes to its Flask server. 126 | 127 | To verify that our server is associated with our app, Slack will make a request to the `/slack` endpoint created by the Flask server in our [Slack Events Adapter](https://github.com/slackapi/python-slack-events-api) by sending a `challenge` parameter that it will expect us to return back. The events adapter will handle this verification for us. 128 | 129 | First, we'll need to add an `/install` route that renders an HTML template where users will eventually be able to install your app. The second route called `/thanks` will return an HTML page to let our users know that our app has been sucessfully installed on their Slack team. 130 | 131 | Let's fire up our app and test that our Flask server is working properly. With your virtual environment turned on and your secrets exported to the environment, go ahead and start your app: 132 | 133 | ```bash 134 | python app.py 135 | ``` 136 |  137 | 138 | Check our your shiny app is running locally in a browser by navigating to [localhost:5000/install](http://localhost:5000/install). :boom: 139 | 140 | ## You Did It! :sparkles: 141 | 142 | You're all set up! Time to check out the next chapter and move on to OAuth. 143 | 144 | ```bash 145 | git checkout chapter-2 146 | ``` 147 | 148 | --- 149 | ###### Documentation Navigation 150 | **Next [Chapter 2](./../docs/Chapter-2.md)** 151 | **Previous [README](./../docs/README.md)** 152 | -------------------------------------------------------------------------------- /docs/Chapter-2.md: -------------------------------------------------------------------------------- 1 | # Chapter 2: OMG OAuth 2 | 3 | Don't you want your mom to be able to install and use your shiny new app on her Bot-Mom's Slack team? In order to have other teams and users authorize your app so they can use it, you'll need to implement an OAuth flow. 4 | 5 | ## Understanding [OAuth](https://api.slack.com/docs/oauth?utm_source=events&utm_campaign=build-bot-workshop&utm_medium=workshop) 6 | 7 | OAuth is a protocol that lets your app request authorization to private details in a user's Slack account without getting their password. Slack provides an [Add to Slack](https://api.slack.com/docs/slack-button) button to help users install your app onto their team. 8 | 9 | When a user who wants to install your app on their Slack team arrives at your installation page, we'll present them with the [Add to Slack](https://api.slack.com/docs/slack-button) button. After giving in to the temptation of a good old fashioned button click, the user will be presented with a menu to select the Slack team they wish to grant your app access to and Slack will redirect the user to another endpoint in your app (in our case, a thank you page) and pass along a temporary authorization code. Your app can then exchange this temporary code for an OAuth token by making a request to Slack's `oauth.access` endpoint. You’ll want to store this token safely and securely, typically in a database, because you’ll use it to gain access to the Slack teams who have installed your app. In this workshop we’ll be storing this in memory for simplicity and urge you to follow [best practices for secure storage of OAuth tokens.](http://api.slack.com/docs/oauth-token-safety) 10 | 11 |  12 | 13 | ## Building an OAuth Flow 14 | 15 | As it stands, our app doesn't have this redirect endpoint built, so let's build it. In `app.py` you'll see we've started a route for you called `/thanks`. This route is what the user will see after they've successfully installed our app. 16 | 17 | Before we return the template to the user, we'll want to grab the temporary authorization code Slack will send to us. 18 | 19 | _app.py_ 20 | ```python 21 | # add this code to the /thanks route 22 | code = request.args.get("code") 23 | ``` 24 | 25 | Then we'll need to exchange that code for an OAuth token. At the top of `app.py` we're importing [slackclient](http://python-slackclient.readthedocs.io/en/latest/) which we'll use to connect to Slack's APIs. After we create an instance of Flask, we create a `client` using this library. We'll use this to make the call to Slack's `oauth.access` endpoint to get our OAuth token. Python-slackclient's docs go over the OAuth flow in more detail [here](http://python-slackclient.readthedocs.io/en/latest/auth.html#the-oauth-flow). 26 | 27 | _app.py_ 28 | ```python 29 | # add this code to the /thanks route 30 | auth_response = client.api_call("oauth.access", 31 | client_id=client_id, 32 | client_secret=client_secret, 33 | code=code) 34 | ``` 35 | 36 | At this point, we'll need to update our installation page with the [Add to Slack](https://api.slack.com/docs/slack-button) button. 37 | 38 | ### Add to Slack Button 39 | 40 | This project has a [templates](templates) folder with an [install](templates/install.html) page started for you. This is where you should put an [**Add to Slack**](https://api.slack.com/docs/slack-button?utm_source=events&utm_campaign=build-bot-workshop&utm_medium=workshop) button! After navigating to the [slack-button page on api.slack.com](https://api.slack.com/docs/slack-button#add_the_slack_button?utm_source=events&utm_campaign=build-bot-workshop&utm_medium=workshop) under the **Add the Slack Button** section you'll see a widget that will generate code for an `Add to Slack` button for your app. Just select your app from the drop down and check only the `bot` scope, then copy the HTML code. 41 | 42 | :warning: _Make sure to select **only the Bot scope**_ 43 | 44 | Back on your [install](templates/install.html) page you can replace the `
` tag placeholder with the code for the button you just copied then fire up your python script and see your shiny new button.
45 |
46 | ```bash
47 | python app.py
48 | ```
49 |
50 | Open up that [/install](http://localhost:5000/install) endpoint again. :boom: Drop :microphone:
51 |
52 | :warning: _Clicking on this button will not work. Don't fret, we'll get it working a little later on._
53 |
54 | ### Tunnel with ngrok
55 |
56 | Now that we've got our endpoint set up properly, we'll need to let Slack know what url to redirect to after a user installs our app using the button we've set up.
57 |
58 | It's time to get `app.py` running. With your virtual environment turned on and your secrets exported to the environment, go ahead and start your app:
59 |
60 | ```bash
61 | python app.py
62 | ```
63 | 
64 |
65 | Check our your shiny app is running locally in a browser by navigating to [localhost:5000/install](http://localhost:5000/install). :boom:
66 |
67 | Since you've already got ngrok installed, in another terminal window, open up an ngrok tunnel for the port your Flask app will be served on locally.
68 |
69 | ```bash
70 | ngrok http 5000
71 | ```
72 | 
73 |
74 | Your terminal output should show both an http and https url ending in `ngrok.io`.
75 |
76 | :warning: _Now is a good time to double check your ngrok tunnel is working properly by visiting **your_ngrok_URL.ngrok.io/install** in a web browser._
77 |
78 | Copy the **https** ngrok url, go to [https://api.slack.com/apps](https://api.slack.com/apps?utm_source=events&utm_campaign=build-bot-workshop&utm_medium=workshop), click on your app and open the **Events Subscriptions** tab. In the **Request URL** form, paste `https://your_ngrok_URL.ngrok.io/slack`. After checking that the **Enable Events** button is turned on, you should see a green notification that your URL has been validated. :tada:
79 |
80 | 
81 |
82 | Once you **Save Changes** you've made on this page by clicking on the green button on the bottom of the page we can move on to the exciting and fun part...
83 |
84 | In your app's settings page under **OAuth Settings** add the ngrok https url you used earlier for the **Redirect URL** and add `/thanks` and **Save Changes**.
85 |
86 | 
87 |
88 | At long last, you'll want to turn on the Events tap by navigating back to the **Events Subscriptions** tab and flipping the **Enable Events** switch to on.
89 |
90 | 
91 |
92 | ## You Did It! :sparkles:
93 |
94 | You're authorized to move onto the next chapter!
95 |
96 | ```bash
97 | git stash
98 | git checkout chapter-3
99 | ```
100 |
101 | ---
102 | ###### Documentation Navigation
103 | **Next [Chapter 3](./../docs/Chapter-3.md)**
104 | **Previous [Chapter 1](./../docs/Chapter-1.md)**
105 |
--------------------------------------------------------------------------------
/docs/Chapter-3.md:
--------------------------------------------------------------------------------
1 | # Chapter 3: Handle Hello with Class
2 |
3 | ## Give Your [Bot](bot.py) Some Class
4 |
5 | If you open up this branch's [bot.py](bot.py) file you'll find some changes. Now we've got a shiny new `Bot` class to play with. By having a Bot class we'll be able to instantiate bot objects that will help us to store useful information and methods in a more easily accessible way.
6 |
7 | We can store bits of information we'll want to access later as attributes on the bot object and actions we want our bot to take as methods.
8 |
9 | Instead of importing the `python-slackclient` library in our `app.py` file like we did before, we'll import it into this file and access the `SlackClient` through an attribute on our bot. This makes it easier to handle OAuth since our bot object's attributes can be accessed in the other places it will live, such as the `app.py` file.
10 |
11 | ### Teach Bot How to Auth
12 |
13 | Previously we were making a call to Slack's `oauth.access` endpoint directly in the `app.py` file. Let's move that functionality into a method in our Bot class.
14 |
15 | We've started an `auth` method that accepts a `code` parameter. This is a temporary authorization code Slack sends in the OAuth flow when a user authorizes your app on their team. Just like before, we'll exchange this code for an OAuth token by making a call to the `oauth.access` endpoint.
16 |
17 | After we've authorized, we'll reconnect to the Slack Client so we can start to have users interact with our bot.
18 |
19 | _bot.py_
20 | ```python
21 | def auth(self, code):
22 | auth_response = self.client.api_call("oauth.access",
23 | client_id=self.oauth['client_id'],
24 | client_secret=self.oauth['client_secret'],
25 | code=code)
26 | # We'll save the bot_user_id to check incoming messages mentioning our bot
27 | self.bot_user_id = auth_response["bot"]["bot_user_id"]
28 | self.client = SlackClient(auth_response["bot"]["bot_access_token"])
29 | ```
30 |
31 | If you peek on over at `app.py` again you'll see we've updated your `/thanks` route to call your bot object's shiny new `auth` method.
32 |
33 | ## Say Hello to your Little Bot
34 |
35 | Now that we've been authorized, let's teach our bot to respond to users who want to say hello!
36 |
37 | Earlier, we subscribed to the Slack Events API endpoint called [message.im](https://api.slack.com/events/message.im). We'll listen for these events, check to see if anyone is saying hello to our bot in a DM, and then build a method on our Bot class to respond.
38 |
39 | ### Event Handlers
40 |
41 | Back over in the `app.py` file you'll see we've started a function for you called `handle_message` which uses the [Slack Events Adapter](https://github.com/slackapi/python-slack-events-api). Here you'll need to write a function to check that the `event_data` sent to your app's endpoint from Slack contains the word "hello" and then respond to the user with the bot method `say_hello` that we'll build a little later.
42 |
43 | Since we only want our bot to say hello when someone says hello to us first, we'll need to look at the `text` field in the `event_data` sent from Slack and only call our Bot's `say_hello` response method if we see the word "hello".
44 |
45 | Here's one way to do that:
46 |
47 | _app.py_
48 | ```python
49 | # Using the Slack Events Adapter, when we receive a message event
50 | @events_adapter.on("message")
51 | def handle_message(event_data):
52 | # Grab the message from the event payload
53 | message = event_data["event"]
54 | # if the user says hello
55 | if "hello" in message.get('text'):
56 | # have our bot respond to the message
57 | mybot.say_hello(message)
58 | else:
59 | # otherwise help us find out what went wrong
60 | print "This isn't the message we expected: \n%r\n" % message
61 | ```
62 |
63 | ### Bot Response Methods
64 |
65 | Once `app.py` can handle the `message` events we want our bot to respond to, we'll need to build a method on our `Bot` class to respond.
66 |
67 | In `bot.py` you'll see we've started a method called `say_hello` to respond to a user's direct message. At this point we'll need to call to the Slack API's `chat.postMessage` endpoint to send our response.
68 |
69 | It should look something like this:
70 |
71 | _bot.py_
72 | ```python
73 | def say_hello(self, message):
74 | """ A method to respond to a user who says hello. """
75 | channel = message["channel"]
76 | hello_response = "I want to live! :pray: Please build me <@%s>" % message["user"]
77 | self.client.api_call("chat.postMessage",
78 | channel=channel,
79 | text=hello_response)
80 | ```
81 |
82 | Let's start our app back up again and test that it's working in the browser! Double check that your virtual environment is turned on, your secrets are exported to the environment and your ngrok tunnel is open.
83 |
84 | ```bash
85 | python app.py
86 | ```
87 |
88 | Then navigate in your browser to the [/install](http://localhost:5000/install) endpoint we've set up and click on your **Add to Slack** button and install your app on the workshop Slack team.
89 |
90 | (HINT: if you're getting an ngrok error trying to install, you may need to restart ngrok and reset your app's **Request URL** under you App Setting's **Event Subscriptions** tab and your **Redirect URL** under the **OAuth & Permissions** tab.)
91 |
92 | Once you've installed your bot successfully you can DM your bot, say "hello" and your bot should respond!
93 |
94 | ## You Did It! :sparkles:
95 |
96 | You should have a working bot! If you're looking for ways to make your bot fancier, we'll go over adding more interaction with [message buttons](https://api.slack.com/docs/message-buttons) and integrating some NLP with a lovely service like [api.ai](https://api.ai/) in the next two chapters.
97 |
98 |
99 | ```bash
100 | git stash
101 | git checkout chapter-4
102 | ```
103 |
104 | ---
105 | ###### Documentation Navigation
106 | **Next [Chapter 4](./../docs/Chapter-4.md)**
107 | **Previous [Chapter 2](./../docs/Chapter-2.md)**
108 |
--------------------------------------------------------------------------------
/docs/Chapter-4.md:
--------------------------------------------------------------------------------
1 | # Chapter 4: Message Buttons Magic
2 |
3 | Building a bot to respond to salutations is an awesome feat, but what if you want to add more excitement :sparkles: to the messages your bot can send? [Message buttons](https://api.slack.com/docs/message-buttons?utm_source=events&utm_campaign=build-bot-workshop&utm_medium=workshop) are one way to make your bot's messages more interactive and interesting.
4 |
5 | ## What's in a [message](https://api.slack.com/docs/messages?utm_source=events&utm_campaign=build-bot-workshop&utm_medium=workshop)?
6 |
7 | If you're having a conversation with your coworker in a conference room in the real world, there's a lot of information that goes into making it happen. Slack Messages are very similar. You'll need to know *where* the message is being sent, *to whom* it's being sent and *what* you want to say.
8 |
9 | Slack uses JSON to communicate all the information about each message sent between users on Slack. Our app can use this context to be helpful at the right times, in the right places and in the most appropriate ways. To learn more about formatting messages in Slack, you can find [helpful documentation here.](https://api.slack.com/docs/message-formatting?utm_source=events&utm_campaign=build-bot-workshop&utm_medium=workshop)
10 |
11 | Let's break down an example JSON message:
12 |
13 | ```JSON
14 | {
15 | "channel": "#general",
16 | "text": "I want to live, <@U123|bob>!",
17 | "attachments": [{
18 | "text": "Please build me."
19 | }]
20 | }
21 | ```
22 |
23 | The `"channel"` field points to where the message will be sent. If sending a message directly to a user through a DM, pass either the id of the user or the id of the DM channel to this field. It is a best practice to send the channel id in this field whenever possible.
24 |
25 | The `"text"` field contains the message's primary content. In our example you may have noticed the special characters `<@` and `>` when we mention the user in the content of our message. These characters will let us use @mentioning functionality to send a notification to the user.
26 |
27 | The `"attachments"` field allows our bot to send messages with even more context. In the example above, we're attaching additional text.
28 |
29 | ### [Message Attachments](https://api.slack.com/docs/message-attachments?utm_source=events&utm_campaign=build-bot-workshop&utm_medium=workshop)
30 |
31 | Enrich your messages with a bit more style, clear formatting and actionable content by adding message attachments. Like a high-five, attachments are fun and exciting, but too many of them can be overwhelming and disrupt the collaborative workflows and conversations that happen within Slack. There's a 20 attachment limit per message. :raised_hands:
32 |
33 | Attachments are a list of objects containing information about each attachment you'd like to send with this message. One example of a message with attachments looks like this:
34 |
35 |
36 |
37 | The JSON for this message looks like this:
38 |
39 | ```JSON
40 | {
41 | "text": "I am a test message",
42 | "attachments": [
43 | {
44 | "text": "And here's an attachment!",
45 | "color": "#FFAA99"
46 | }
47 | ]
48 | }
49 | ```
50 |
51 | Message buttons are a type of attachment. To use buttons in your messages, include an array called `"actions"` with your attachments, with one object for each button. For example, if we want to ask a user what operating system they are using, it might look like this:
52 |
53 |
54 |
55 | Here's what that would look like in JSON:
56 |
57 | ```JSON
58 | {
59 | "text": "I want to live! Please build me.",
60 | "attachments": [
61 | {
62 | "pretext": "I'll tell you how to set up your system.:robot_face:",
63 | "text": "What operating system are you using?",
64 | "callback_id": "os",
65 | "color": "#3AA3E3",
66 | "attachment_type": "default",
67 | "actions": [
68 | {
69 | "name": "mac",
70 | "text": ":apple: Mac",
71 | "type": "button",
72 | "value": "mac"
73 | },
74 | {
75 | "name": "windows",
76 | "text": ":fax: Windows",
77 | "type": "button",
78 | "value": "win"
79 | }
80 | ]
81 | }
82 | ]
83 | }
84 | ```
85 |
86 | Let's add this to our bot's `say_hello` method!
87 |
88 | _bot.py_
89 | ```python
90 | def say_hello(self, message):
91 | """
92 | A method to ask workshop attendees to build this bot. When a user clicks
93 | the button for their operating system, the bot should display the set-up
94 | instructions for that operating system.
95 | """
96 | hello_message = "I want to live! Please build me."
97 | message_attachments = [
98 | {
99 | "pretext": "I'll tell you how to set up your system. :robot_face:",
100 | "text": "What operating system are you using?",
101 | "callback_id": "os",
102 | "color": "#3AA3E3",
103 | "attachment_type": "default",
104 | "actions": [
105 | {
106 | "name": "mac",
107 | "text": ":apple: Mac",
108 | "type": "button",
109 | "value": "mac"
110 | },
111 | {
112 | "name": "windows",
113 | "text": ":fax: Windows",
114 | "type": "button",
115 | "value": "win"
116 | }
117 | ]
118 | }
119 | ]
120 | channel = message["channel"]
121 | self.client.api_call("chat.postMessage",
122 | channel=channel,
123 | text=hello_message,
124 | attachments=json.dumps(message_attachments))
125 | ```
126 |
127 | With our virtualenv on and prepped with our app's secrets, we can fire up our app again to test our handiwork. Once you've got the app started locally, your ngrok tunnel is started and your **Request URL** and **Redirect URL** are updated, you'll need to reinstall your app using the `/install` endpoint.
128 |
129 | Then say "hello" to your bot in a DM to test it out!
130 |
131 | ## Life after Button Pressing
132 |
133 | Who can resist a good button click? You may have noticed your buttons don't do anything. Like asking a kid to drive to the store and pick up some milk, our bot doesn't know how to handle what we're asking it to do.
134 |
135 | ### Interactive Message Request and Response URLs
136 |
137 | When a user clicks on a button you've attached to your message, Slack sends a request with some JSON to a url you specify in the **Interactive Messages** section of your **App Settings** page.
138 |
139 | Let's create a route on `app.py`'s `events_adapter` server called `/after_button` that will listen for interactive message requests from Slack.
140 |
141 | _app.py_
142 | ```python
143 | @events_adapter.server.route("/after_button", methods=["GET", "POST"])
144 | def respond():
145 | """
146 | This route listens for incoming message button actions from Slack.
147 | """
148 | slack_payload = json.loads(request.form.get("payload"))
149 | # get the value of the button press
150 | action_value = slack_payload["actions"][0].get("value")
151 | # handle the action
152 | return action_handler(action_value)
153 | ```
154 |
155 | Now that we've built an endpoint to receive these events, we can append that to the end of our ngrok url and add it to the **Request URL** form in the **Interactive Messages** section of your **App Settings** page.
156 |
157 |
158 |
159 | ### Action Handlers
160 |
161 | Next we'll need to build an `action_handler` like we did for handling incoming events. When you open `app.py` you'll see we've started this for you.
162 |
163 | Once Slack sends a JSON payload of the action the user took to your endpoint, you'll have 3 seconds to respond to the request. In our example, we'll return a response right away. You can respond to the user's action using the `response_url` parameter of the JSON payload sent by Slack to respond up to 5 times over 30 minutes.
164 |
165 | _app.py_
166 | ```python
167 | @events_adapter.on("action")
168 | def action_handler(action_value):
169 | if action_value == "mac":
170 | return make_response(mybot.show_mac(), 200, {'Content-Type':
171 | 'application/json'})
172 | if action_value == "win":
173 | return make_response(mybot.show_win(), 200, {'Content-Type':
174 | 'application/json'})
175 | return "No action handler found for %s type actions" % action_value
176 | ```
177 |
178 | ### Bot Response Methods
179 |
180 | Now that we've laid the foundation for our bot to respond to the actions a user can take with message buttons, we should give our bot something new to say.
181 |
182 | Our bot wants to help you. Once a user tells our bot what operating system they are running, it should give that user instructions on how to build the bot within that operating system.
183 |
184 | We've written a bot method for responding with instructions for setting up a Mac operating system called `show_mac`. Let's show those Windows users some :hearts: and write a `show_win` method.
185 |
186 | This is what the message will look like:
187 | 
188 |
189 | _bot.py_
190 | ```python
191 | def show_win(self):
192 | message = {
193 | "as_user": False,
194 | "replace_original": False,
195 | "response_type": "ephemeral",
196 | "text": ":fax: *Windows OS*:\n Here's some helpful tips for "
197 | "setting up the requirements you'll need for this workshop:",
198 | "attachments": [{
199 | "mrkdwn_in": ["text", "pretext"],
200 | "text": "*Python 2.7 and Pip*:\n_Check to see if you have "
201 | "Python on your system:_\n```python --version```\n_Download "
202 | "link:_\nhttps://www.python.org/ftp/python/2.7.12/python-2.7.1"
203 | "2.msi\n_Make sure to tick `Add Python.exe to PATH` when "
204 | "installing Python for Windows._\n_If that doesn't add it to "
205 | "the path after installation, run this command:_\n```c:\pyth"
206 | "on27\\tools\scripts\win_add2path.py```\n_After downloading "
207 | "Python, you must upgrade your version of Pip:_\n```python "
208 | "-m pip install -U pip```\n*Virtualenv*:\n_Check to see if "
209 | "you have virtualenv on your system and install it if you "
210 | "don't have it:_\n```virtualenv --version\npip install "
211 | "virtualenv```\n*Ngrok:*\n_Check to see if you have ngrok on "
212 | "your system:_\n```ngrok --version```\n_Download "
213 | "Link:_\nhttps://bin.equinox.io/c/4VmDzA7iaHb/ngrok-stable"
214 | "-darwin-amd64.zip\nTo unzip on Windows, just double click "
215 | "ngrok.zip",
216 | "footer": "Slack API: Build this Bot Workshop",
217 | "footer_icon": "https://platform.slack-edge.com/img/default"
218 | "_application_icon.png"
219 | }]}
220 | return json.dumps(message)
221 | ```
222 |
223 | At this point, you can test out your Bot by installing it on your team and saying hello over DM.
224 |
225 | Go ahead, give those message buttons a whirl!
226 |
227 | ## You Did It! :sparkles:
228 |
229 | Not satisfied with the intelligence of your bot? In the next chapter we'll teach your bot some natural language processing with API.AI.
230 |
231 | ```bash
232 | git stash
233 | git checkout chapter-5
234 | ```
235 |
236 | ---
237 | ###### Documentation Navigation
238 | **Next [Chapter 5](./../docs/Chapter-5.md)**
239 | **Previous [Chapter 3](./../docs/Chapter-3.md)**
240 |
--------------------------------------------------------------------------------
/docs/Chapter-5.md:
--------------------------------------------------------------------------------
1 | # Chapter 5: ABCs of NLP
2 |
3 | ### Understanding NLP
4 |
5 | Natural Language Processing is a field of computer science dedicated to programmatically understanding the ways actual humans communicate with each other. Anyone who's said or written something that another person has understood to mean something other than what you intended to communicate knows the complexity of natural human language. The field of NLP itself has made vast improvements in processing human language over the last few years, which is great for us bot builders!
6 |
7 | NLP algorithms learn how to mimic natural human language by analyzing a sample of the sort of human language you're aiming to mimic, often called a training set. Just like you learned not to run around screaming in the classroom like you might on a playground as a kid, our bot's communication should be well suited to the context and purpose it serves. Selecting a training set that is an accurate representation of the tone you'd like to convey is often vitally important.
8 |
9 | There's a variety of great libraries and tools you can use to process natural language like [Natural Language Toolkit](http://www.nltk.org/), [scikit-learn](http://scikit-learn.org/stable/) or IBM Watson's [Natural Language Classifier](https://www.ibm.com/watson/developercloud/nl-classifier.html). We'll use an API service called [api.ai](api.ai) which will provide us with already trained algorithms and preloaded training sets so we can get a head start.
10 |
11 | ## Setting up api.ai
12 |
13 | First, you'll need to create an account on [api.ai](api.ai) or log in if you've already signed up. Once you've registered and logged in, you'll want to create a new agent by clicking on the **Create Agent** tab on the top left navigation.
14 |
15 | Agents are used to organize the logic and context for different bots or apps. It will also serve as the connection between our app and the NLP service api.ai provides. This is where we fine tune the NLP service to our particular use case.
16 |
17 | The name for your agent should match the name of the app or bot you're creating it for. Add a description for your agent and leave the sample data field blank. This is where you can add extra training data to further customize the tone and understanding of your bot.
18 |
19 | We want our bot to be able to recognize the **intent** behind what a user is saying rather than strictly listening for exact matched words. In our example we want to recognize when a user intends to greet us, so we'll use api.ai's **Intents**.
20 |
21 | To add a new intent, select **Intents** from the top left navigation bar. Let's call this intent *Greet*.
22 |
23 |
24 |
25 | Once we've created a new intent we'll need to train our agent to recognize different phrases that have the same meaning. In the **User says** box type some different greeting phrases you'd like the bot to recognize.
26 |
27 |
28 |
29 | Once you've added 3 to 5 different phrases, click the blue save button at the top and let's test our agent. Type a different greeting phrase into the test console on the right side of the page. If your agent is trained correctly it should recognize the intent like this:
30 |
31 |
32 |
33 | If your agent is not recognizing the intent properly, don't fret! Simply add a few more phrases into the **User says** box and try again. Once your agent is sucessfully trained on recognizing a greeting, we'll need to wire it up to our bot.
34 |
35 | ## Your Bot and Agent Become Best Friends
36 |
37 | Our Bot and Agent would work so well together if they just knew how to speak the same language. Luckily, api.ai has provided a SDK that speaks the same language our Bot speaks; Python! :snake:
38 |
39 | In the terminal window where you've been running the `app.py`, make sure your virtualenv is activated and install api.ai's Python SDK with this command:
40 |
41 | ```bash
42 | pip install apiai
43 | ```
44 |
45 | Next, we'll need to add our Agent's API key (called a Client Access Token in api.ai) to our environment.
46 | You can find your client access token by clicking the settings icon next to your agent's name in the main navigation header on the left hand side. Copy the **Client access token** Under the **API Keys** section.
47 |
48 |
49 |
50 | To export apiai's secret keys to your environment in bash:
51 |
52 | ```bash
53 | export CLIENT_ACCESS_TOKEN='123xxx45xx6789'
54 | ```
55 |
56 | If you've got a Windows machine, use:
57 |
58 | ```bash
59 | set CLIENT_ACCESS_TOKEN='123xxx45xx6789'
60 | ```
61 |
62 | Once we've got this connection set up we'll need to alter our Bot's code and teach it how to ask the Agent for some help.
63 |
64 | ### Teach Your Bot To Ask For Help
65 |
66 | If you open up `bot.py` you'll see some changes to the Bot class. At the top, we've imported the api.ai library which we're using in the `__init__` function to give our bot object a `self.ai` attribute which stores an api.ai client that we'll use to communicate with the agent. It's like teaching our bot it's new agent friend's phone number.
67 |
68 | We'll need to use this attribute to build a method to `try_to_understand` the incoming text from a user, like phoning a friend to ask for help. Let's finish building the method we've started for you.
69 |
70 | _bot.py_
71 | ```python
72 | def try_to_understand(self, users_text):
73 | """
74 | A method to query api.ai's NLP algorithm and help our bot understand a
75 | broader range of language.
76 | Returns the intent matched from the query
77 | """
78 | # first we create a properly formatted http request to send to api.ai
79 | ai_request = self.ai.text_request()
80 | # then we set the user's text as the query we want to check for intent
81 | ai_request.query = users_text
82 | # get the response from the query
83 | ai_response = ai_request.getresponse()
84 | # parse the json into a dictionary
85 | ai_response_dict = json.loads(ai_response.read())
86 | # get the name of the intent that the agent has matched
87 | intent = ai_response_dict["result"]["metadata"].get("intentName")
88 | return intent
89 | ```
90 |
91 | When the api.ai agent does not recognize the intents you've trained it to recognize it will return a _default fallback intent_.
92 | We can use this new ability to recognize the intended meaning in a user's text to appropriately respond.
93 |
94 | ### Handle Events with Intents
95 |
96 | To make use of our bot's new skillz, let's update the routing layer of our application. Specifically, we'll need to alter the `event_handler` in _app.py_.
97 |
98 | _app.py_
99 | ```python
100 | @events_adapter.on("message")
101 | def handle_message(event_data):
102 | message = event_data["event"]
103 | users_text = message.get('text')
104 | # Instead of matching a literal string, we'll send
105 | # the user's text through apiai's NLP algorithm to see if it matches
106 | # the intended message we're looking for
107 | user_intent = mybot.try_to_understand(users_text)
108 | # if the user's intent matches the Greet intent
109 | if user_intent == "Greet":
110 | # then say hello!
111 | return mybot.say_hello(message)
112 | print "This isn't the message we expected: \n%r\n" % message
113 | ```
114 |
115 | Once you've updated this code, you can test it out.
116 |
117 | As a reminder, you'll want to make sure your virtualenv is activated, your secrets are exported to your environment and your ngrok tunnel is on. If you've restarted your ngrok tunnel, you'll also need to update the the https urls in your Slack app's settings page (including **Redirect URL** on **OAuth & Permissions** tab, **Request URL** on **Interactive Messages** tab, and **Request URL** on **Events Subscriptions** tab).
118 |
119 | After that, start your local server with `python app.py` and open [http://localhost:5000/install](http://localhost:5000/install) in your favorite web browser and follow the button clicking magic to install your newly educated bot the workshop team for testing.
120 |
121 | Once your app is installed, try out various ways to say hello to your bot via DM!
122 |
123 | ## You Did It! :sparkles:
124 |
125 | We can't wait to see what you build next!
126 |
127 | Thanks for joining us!
128 |
129 | ---
130 | ###### Documentation Navigation
131 | **Next [Give Us Feedback!](https://goo.gl/forms/8FlqD5roZtCl7wx92)**
132 | **Previous [Chapter 4](./../docs/Chapter-4.md)**
133 |
--------------------------------------------------------------------------------