oh no… a Slack app
Sometimes life just doesn’t go your way — sometimes you get your comedic timing wrong, you get covered in a bit of dog slobber, you’re just not in the mood — in those times the only thing you can do is say oh no. Luckily you no longer need to use your vocal cords to say oh no, I have create a Slack app that will allow you to express your exasperation through a simple command. Watch the GIF below to see it in action.
/ohno
Webcomicname is a hilarious webcomic created by Alex Norris, check out the wembcomicname blog, read his funny tweets, and go buy something from his store! Thank you Alex for creating the blob.
Additionally, thank you to caseyf for creating the webcomicname mashup app, and inspiring me to code my own oh no app.
Slack is an enterprise communication tool that is used in businesses and teams to chat about all the things — important or irreverent — you might use it at work.
If you like what you see then read the section below on how to use the Slack app.
If you’re a techie like me then read even further below to see how it was made and how to create your own slack app.
Installation and Usage
“oh no, this is exactly what I need, how do I install and use it?”
This is a Slack app so needs to first be added to your Slack workspace by someone with the correct permissions. You can try copy and pasting the URL below into your browser and following the instructions to add it to your workspace, if you are unable to install it then plead with your administrator to do it for you.
https://slack.com/oauth/authorize?client_id=229509065204.490580425281&scope=commands
Once installed, simply type /ohno in any channel or chat and it should work. You can also try:
- /ohno comic - post a random comic from webcomicname.com
- /ohno mashup - post a mashup comic from webcomicname-mashup.glitch.me
/ohno comic
How is it made?
TLDR; All the code is open-sourced on GitHub: https://github.com/bhishp/oh-no-slack.
The app consists of a very simple Node/Express app that responds to an OAuth 2.0 request and a single ‘/’ command with some optional parameters. Node is a JavaScript runtime that executes JS on the server, Express is a web application framework for Node that is commonly used for building web apps. This tutorial assumes you already have a working knowledge of the above — if not, there are many great tutorials out there to get you started.
Building this app was broken in to the following parts:
- Create an app in Slack’s API interface — https://api.slack.com/apps/
- Create a Node app that handles an OAuth 2.0 request for installation and responds to ‘/’ commands
- Building messages to be rendered in the Slack interface
- Host the app on Heroku
- BONUS: create a script for querying the Tumblr API for comics uploaded to webcomicname.com
- BONUS: create a script for querying split comic panels used for the mashup app
Slack has a great set of resources for developers, including tutorials and documentations for building slack APIs here.
Create slack app and node service
The official tutorial on using ngrok to develop locally contains 90% of what we need for our simple app so we’ll follow the instructions there and add as we go along.
ngrok is a great tool that allows you to expose services running locally on your own machine to the internet — this means that slack can send HTTP requests directly to the node service running on your computer!
Open the tutorial and follow the steps for installing ngrok, building a node server and creating your slack app.
OAuth and Permissions
During Step 1 it says “In the redirect URI field, paste your ngrok forwarding address…” — Slack may not actually ask you for your redirect URI, in this case you must navigate to OAuth and Permissions section and enter your URL in the Redirect URLs section.
Once you have completed all the steps in the tutorial you should now have a slack app running from your local machine that responds to the command ‘/command’ with a simple text message ‘Your ngrok tunnel is up and running!’.
Congratulations, you have built your first slack app!! This is great but we don’t just want to send some text to our users, we want them to see the infamous blob. See the next section below on how we can create some more complex responses.
Be aware that your ngrok url may change every time you restart the ngrok command (such as closing your terminal session). If this happens then you must remember to update your redirect URL and Command Request URL in your app configuration. Alternatively, you can pay $5/month to have up to 3 reserved domains: https://ngrok.com/pricing.
Note, ngrok should only really be used for development purposes, when we actually release the slack app for usage in the real-world we don’t want the server to be running from your laptop all day. The last step here is to host your app on Heroku , which will have a consistent URL and means your laptop doesn’t have to be switched on and running the node service to work.
Responding to Slack with images
Slack is all about messages, messages, messages. These can range from just simple text to more complex and rich content. Slack has an explanation about messages in their docs, what we’re interested in is attaching content. Attachments allow us to respond with extras, such as a title, image , thumbnail, author info and more. Rather than just sending plain text in a response, we can instead respond with a json object to describe the complex message.
// boring plain-text message
res.send(“ngrok is running..”)
// give it some pizzazz with slack markup
res.send(“ngrok is *boldly* running your `code` with its ~striking~ _italics_ ..”)
// the real deal, a json object with an attachment
res.send(
{
response_type: "in_channel",
attachments: [
{
color: "#fe7db5",
fallback: "oh no...",
image_url: "https://api.tumblr.com/v2/blog/webcomicname/avatar/512"
}
]
}
);
The final json is the real code that the oh no slack app uses to respond to the /ohno slack command. You’ll notice we return a single object with the following properties:
response_type (“ephemeral” | “in_channel”) — this says whether the response message will be shown only to the user who called the command, or to the entire channel.
attachments (array of attachment objects) — notice this is an array, we can send up several attachments in a single message (though it is recommended to send no more than 20).
Our attachment object has the properties:
- color — format our message with a familiar pink
- image_url — the url of the image we want to render. webcomicname tumblr blog has the oh no character as an avatar so we can call their API directly
- fallback — fallback text if the image can not be loaded (mandatory when image_url is present)
Slack has a cool tool called the Message Builder which allows you to write your JSON in the browser and see a preview of the slack message in realtime, have a play around with it and see what cool type of responses you can build. For example, we could paste in the JSON for our response and see our blob:
https://api.slack.com/docs/messages/builder
This is still a rather simple response, take a look at the docs for an example of all the properties an attachment can contain here.
Now we have constructed a nicer message we need to update the code to and Slack configuration to respond with this when the command ‘/ohno’ is called from within slack.
Firstly, navigate to Slash Commands in the slack app configuration and add a new command. Call the command /ohno and point it to your ngrok url, e.g.: https://92832de0.ngrok.io/ohno
Next, update your code to handle a post request to /ohno, which responds with the message we have just constructed:
app.post("/ohno", (req, res) => {
// oh no...
res.send(
{
response_type: "in_channel",
attachments: [
{
color: OH_NO_COLOR,
fallback: "oh no...",
image_url: "https://api.tumblr.com/v2/blog/webcomicname/avatar/512"
}
]
}
);
});
Go to slack and send a message /ohno and you should see your blob! Well done.
When creating slash commands be aware that there is a global namespace for slash commands in slack. This means that your command name may conflict with a totally random app installed on the same workspace, the rules of which are called are described here. This mean, make your command names memorable yet distinct enough that you wont clash with another slack app (/wave might not be a good name for your surfing command).
Host your slack app on Heroku
Amazing, you’ve made your first slack app! But you go to bed and switch off your laptop and it stops working… oh no. But do not fear, we can use Heroku’s free tier to keep your app up and running. Well, most of the time (see free tier explanation below).
Heroku is a PaaS (Platform as a Service) cloud provider, meaning we can just pass it our app bundle and it handle the hosting of the app for us. We do not need to configure any infrastructure, load balancers, networks, etc — it handles all that for us, pretty neat. If you are not so keen on Heroku there are plenty of alternative options out there, see here for an extensive list.
Heroku’s free tier number of hours is slightly less than a full month — and they will automatically put your dyno (server instance) to sleep when it is not sending/receiving network traffic to preserve your hours during inactivity. Heroku will automatically re-awaken your dyno if it receives a request, though this means your users will probably not receive a response if they are the one waking your dyno.
Before we proceed; the way I have deployed this app is via a GitHub integration, meaning your code must be hosted on your GitHub account. However, we want to make sure that we do not publicly host your secret Slack credentials! If you are going to deploy this way then it is worth scrolling to Bonus section at the end of this tutorial about using dotenv for providing credentials.
To host our app on Heroku we need to:
- Sign up to Heroku
- Create a new app and follow the steps of the workflow, I called my app oh-no-slack and hosted it in Europe
- Let’s add those environment variables so our app can communicate with slack. Select Settings from the toolbar and the Reveal Config Vars button, enter the same vars we have been using so far
- Next we need to deploy the code to the Heroku app, my code is hosted on GitHub meaning I can just use the GitHub integration for deployment directly from the repo
- Select Deploy from the toolbar and click the GitHub deployment method to connect to the git repo on your account.
- There should now be a button to deploy a branch of the code, right now mine just has a master branch. Click it and you’ll see the deployment logs in realtime
- Huzzah! We now have our app running in the ‘cloud’. Heroku should automatically detect this is a node app and run your npm start script. It will also give us http and https URLs to access it, mine is: https://oh-no-slack.herokuapp.com/
- Finally, we need to tell slack to look at this new URL for authentication and POSTing commands
- Open your Slack App configuration and select OAuth & Permissions, update your redirect URL to point to Heroku (e.g. https://oh-no-slack.herokuapp.com/oauth)
- Go to Slash Commands and change your command Request URL to point to Heroku as well (e.g. https://oh-no-slack.herokuapp.com/ohno)
- Now open up your slack chat, type ‘/ohno’ and see the magic happen
At this point you can give yourself a big pat on the back, well done. But… oh no, you promised us two bonus steps on creating the ‘comic’ and ‘mashup’ commands. I did intend to but I have blabbed too much already, so maybe one day I will create some follow-up posts on how it was done, otherwise just go and check out the full code yourselfand snoop around.
Thanks for taking the time to read, I hope it was helpful. Please post any questions as comments and I will happily get back to you. In the meantime, I leave you with this wise observation.
http://webcomicname.com/post/163716486209
Extra: Use dotenv to load credentials
As the tutorial says, “Do not upload this file to a public GitHub repository until you relocate variables such as the Client ID and Secret to environment variables. Keep your Client Secret safe!”. This is sound advice because anyone can look in your git repo, or history (even if you’ve removed them in a later commit), and steal your credentials for malicious use.
Environment variables are a great way to separate config from code, as described in the 12-factor app, and, fortunately, there is a neat little NPM library that makes this a bit easier for us to manage. dotenv is a package that will allow us to create a .env file which lists all the environment variables to be loaded on application startup. We just need to:
- install dotenv
- create the .env file
- load dotenv in our script
- access the secret via node process.env
- **mark git to ignore the file ** — this is very important otherwise we’ll end up committing our credentials anyway!
Install the package.
# with npmnpm install dotenv
# or with Yarnyarn add dotenv
Create a file called .env in the root of the project
SLACK_CLIENT_ID=123456789.987654321
SLACK_CLIENT_SECRET=abc123xyz987def456
Load dotenv
# notice I only load dotenv in the dev script, the start script will be used by Heroku and we will add the environment variables directly via the Heroku app configuration
"scripts": {
"start": "node index.js",
"dev": "node -r dotenv/config index.js"
},
Access the environment variables
// in index.js
// access the environment variables and store them in a constantconst clientId = process.env.SLACK_CLIENT_ID;
const clientSecret = process.env.SLACK_CLIENT_SECRET;
...
// use them when calling slack's oauth.access
request({
url: "https://slack.com/api/oauth.access",
qs: {
code: req.query.code,
client_id: clientId,
client_secret: clientSecret
},
method: "GET",
}, (err, res, body) => {
...
Create a file called .gitignore in the root of your project and add .env
# .gitignore file
.env
# other things you ignore..
node_modules/
npm-debug.log*
Remember, even if you commit credentials and then remove them in a later commit they will still be in the commit history. If you have ever committed your creds in your app then it is best to be safe and regenerate your keys via the slack web interface.