Slackbot with AWS Lambda // Bourbon Blackberry Shrub
If you're looking for the booze to give yourself a Ballmer peak, skip below...The Code
Slack offers a ton of really neat integrations. However, in the setting where I use it, most of them aren't an option. Instead, I recently attended a coding night and spent some time rolling my own simple Slackbot using Amazon Web Services' Lambda and API Gateway features: Slackbroker!
Slackbroker is triggered using a slash command followed by a list of stock symbols. It looks up the current stock price (or the closing price if the markets are closed) of each symbol, then posts publicly to the room where the slash command was run in the first place reporting the price. There are a few barriers here thanks to the way slash commands work in Slack:
- Slash commands call a URL with a payload, but AWS Lambda needs to be invoked by its ARN
- The payload from a slash command is delivered in URL format (contrary to Slack documentation), but AWS Lambda demands input in JSON
- Slash commands respond by displaying a private message to the user who invoked them, but I wanted an individual to bring the whole group's attention to a stock
The Business Logic
To be frank, I finished this part last, but for the tutorial's sake I'll explain the entire Lambda contents here instead. Lambda is pretty easy to set up - by default it accepts your code in its web editor. While I can't fully get behind it for doing something significant with Lambda, it makes it convenient for small hacks like this.
It's in Python. lambda_handler is a special function that serves as the entry point executed when someone invokes your Lambda's ARN. Because of the way the URL data is converted later, we get our content in a dictionary inside of event (also a dictionary) at entry postBody, which maps fields designated in the Slack documentation to their values.
text is a string with everything following the slash command prompt typed in Slack; that is, if a user types /ticker abc def, text looks like 'abc def'.
channel_name is just that - the name of the channel the user was in when they invoked the slash command. This is helpful to ensure you don't spam a general channel when you have Slackbot post back for you.
This weird URL is actually just a Slack incoming webhook. They're pretty trivial to set up, so I won't really get into it, but this is the icky bit that lets Slackbroker post back to the entire group, instead of replying in a hidden message.
get_quote is a simple web scraper and is mostly stolen from a great StackOverflow post, but that code references an automatically-named tag in the regular expression - that is, that code only works for one symbol (and the tag name needs to be manually determined for that symbol to work). I found that there was also a human-readable tag that contains the price, and referenced that in my regex instead.
pretty_quotes is a neat one-liner that uses join and a foreach to turn an array of strings into a single cute comma-separated list for human consumption. I admit to lack of Python fu; an old classmate gave me a lot of help with this line.
text is a string with everything following the slash command prompt typed in Slack; that is, if a user types /ticker abc def, text looks like 'abc def'.
channel_name is just that - the name of the channel the user was in when they invoked the slash command. This is helpful to ensure you don't spam a general channel when you have Slackbot post back for you.
This weird URL is actually just a Slack incoming webhook. They're pretty trivial to set up, so I won't really get into it, but this is the icky bit that lets Slackbroker post back to the entire group, instead of replying in a hidden message.
get_quote is a simple web scraper and is mostly stolen from a great StackOverflow post, but that code references an automatically-named tag in the regular expression - that is, that code only works for one symbol (and the tag name needs to be manually determined for that symbol to work). I found that there was also a human-readable tag that contains the price, and referenced that in my regex instead.
pretty_quotes is a neat one-liner that uses join and a foreach to turn an array of strings into a single cute comma-separated list for human consumption. I admit to lack of Python fu; an old classmate gave me a lot of help with this line.
Calling Lambda via URL
Luckily, AWS has a product called API Gateway that solves exactly our problem - it exposes (ugly) URLs that, when called, deliver the payload on to a specified ARN. Here is how to make a new API Gateway and point it at your Lambda:
- Create a new API through API Gateway. Name it whatever, nobody will see it but you.
- It looks pretty empty, so Create Method. We want a POST with integration type Lambda Function. Pick the region where your Lambda lives (this is found in the top right corner of the AWS console) and enter the name of your new Lambda.
Now you have a nice-looking flow diagram! But it's not active. Make sure you Deploy API, creating a new stage if you didn't have one.
Converting URL Data to JSON
We have a problem, though. Slack will still send its data in a URL-encoded string, but Lambda expects JSON for it to turn into a Python dictionary! Luckily, someone thought of this, and API Gateway allows you to write a converter between types. Head to the Integration Request block of your API, expand Mapping Templates, and add a new one. Content-Type refers to the type of the incoming data; in our case it's application/x-www-form-urlencoded. Change the mode from Input Passthrough to Mapping Template; you will be presented with an editor to write your template. We chose a pretty simple one; $input is a special value in the template.
{
"postBody" : $input.json("$")
}
This defines what the event object from above will look like - we get an entry postBody that contains a map of all of our arguments from Slack. Great, now we can actually invoke our Lambda correctly without it complaining about receiving literally anything besides JSON! (It's picky, isn't it?)
Adding Your Slash Command to Slack
We finally have everything ready to set up the Slack side of things! To get ready, from your API Gateway console, you can use the Resources dropdown as shown here to select Stages; once you do so, choose the stage you created earlier. Grab the Invoke URL from here.
Then, head to my-team.slack.com/services/new and add a new Slash Command (way down in the DIY section). Name it, and in the URL field, paste that Invoke URL from your API Gateway. Leave the method as POST and customize the rest of the form as you see fit; save it and give it a whirl!
After following all these steps, you should have a working Slackbroker. If I missed something, please let me know in the comments. Good luck!
And hey -- do you need a drink after all that? You're in luck, because so did I....
The Booze
A friend of mine started telling me about shrubs, these fruity vinegar syrups. I tried making my own and it was none too pretty - somehow whatever I had created just made me feel sick every time I drank it! But it tasted great, and my interest in shrubs didn't wane - I just decided to leave it to the professionals. Recently I tried a blueberry lavender one at Finger Lakes Cider House in Ithaca, NY and it had to come home; I discovered it pairs great with bourbon!
Bourbon Blueberry Shrub
1.5 oz bourbon
.75 oz Good Life Cider blueberry-lavender shrub
club soda
ice
Fill a tumbler with ice. (Mine's a chilly Death Star!) Add the bourbon and shrub; top with club soda and stir. Condescendingly explain to all your friends what a shrub is and enjoy.