My colleague Rajat recently built a Behind The Scenes photo sharing webapp. The idea behind this app was simple - At Fermyon, we’re a fully remote company. We tend to use Slack for all our communication and we have a channel named #behind-the-scenes where we share photos from all our travel to conferences, and meetups etc. The idea was to create a web app that was a front end to the photos shared on that channel. (You can check out this tutorial on how Rajat built this using Spin and Nuxt.js)
A cool feature of this workflow was that not all photos shared on the channel were displayed on the Behind The Scenes app. Photos would only appear on the site if they were ‘signed off’ by the person who posted the image. This sign off method was rather clever - It would be via a specific Slack emoji reaction! I loved that little implementation detail so set out to see how it was built out.
Here’s how you can can build a webhook that is triggered with a Slack emoji reaction, in less than 20 lines of code!
Create a Slack App
To build this webhook you first need to create a Slack app.
1. Go to api.slack.com and click on ‘Your Apps’
2. Click the ‘Create New App’ button and add an App Name (you can change this later) and also a workspace that you want to develop your app in. Ensure you have the permissions to add App integrations to Slack. Personally, I prefer having a private Slack workspace for testing out bots as I’d rather not alarm my colleagues with an erratic Slack bot 🙃
3. Nice! Your Slack app is created. We now need to add an Event Subscription to it so that it can detect when an emoji reaction is added to a message. Click on the ‘Add features and functionality’ section and then the ‘Event Subscriptions’ button
4. The Slack API has an interesting way to ensure only ‘Verified’ URLs can respond to Events. Essentially, their API sends a HTTP POST request to your endpoint, that contains a challenge
parameter. They expect your endpoint to respond with the same challenge
value to verify your endpoint.
This is what the payload looks like:
{
"token": "Jhj5dZrVaK7ZwHHjRyZWjbDl",
"challenge": "3eZbrw1aBm2rZgRNFdxV2595E9CY3gmdALWMmHkvFXO7tYXAYM8P",
"type": "url_verification"
}
See the documentation here for more info.
Hold on, I need a backend endpoint to verify the Events API and then when someone adds an emoji to a Slack message.
Here’s where Spin comes in 👉🏽
Create an endpoint using Spin
Spin is an open source developer tool for building and running serverless applications powered by WebAssembly. To install Spin, check out the guide here.
1. Once you’ve installed Spin, let’s create a new app. This guide has code samples in Python but you can choose any of the other languages in the Spin SDK to get started:
spin new
Pick a template to start your application with: http-py (HTTP request handler using Python)
Enter a name for your new application: reaction-emoji-bot
Description:
HTTP path: /...
cd reaction-emoji-bot
code .
2. That was easy! Now let’s get our hands dirty with some code. Open the folder in your favourite code editor and look for app.py
which is the starting point for your code. We have to first write code to verify the Slack Events API. We can do so by parsing the request
object for the challenge
parameter and returning it via the response
def handle_request(request):
data = json.loads(request.body)
challenge = data.get('challenge', None)
return Response(200,
{"content-type": "text/plain"},
bytes(challenge, "utf-8"))
3. The code above is straightforward - You can extract the challenge
parameter by parsing the request.body
. The response is literally just the challenge
parameter. The Slack API is actually flexible in how you can send this value back. According to them you can verify the request's authenticity and then respond in plaintext or with application/x-www-form-urlencoded
or even in JSON.
- Given that this needs to be a ‘live’ endpoint - we have to build and deploy this. Go back to your terminal and type in
spin build
which will build your Spin app. In case you’ve built the above using TypeScript, do anpm install
before to install some dependancies. - Now that the Spin app is built, deploying it to the cloud is straightforward. Just do a
spin deploy
and the app deploys to the Fermyon Cloud. (Assuming you’ve already logged in to the Fermyon Cloud)
> spin deploy
Uploading reaction-emoji-bot version 0.1.0 to Fermyon Cloud...
Deploying...
Waiting for application to become ready.................. ready
Available Routes:
reaction-emoji-bot: https://reaction-emoji-bot.fermyon.app (wildcard)
Congratulations! Your endpoint is now live. All we need to do now is give the appropriate permissions for your Slack app to access messages, and then write code to listen to the emoji-added event.
Listening to the Events API
1. Let’s first give your Slack App permissions to read messages on Slack. On api.slack.com, click on ‘Oauth & Permissions’ in the left menu that’s in the ‘Features’ section.
2. Scroll down to the ‘Scopes’ section and add the following scopes to your Slack App. Depending on what your app does, it may require more permissions.
chat:write, im:history, incoming-webhook, reactions:read
You also need to subscribe to bot events. In the ‘Event Subscriptions’ section, scroll down to ‘Subscribe to bot events’ and add the following.
message.im, reaction_added
Again, if you expand the scope of your bot, ensure you add the relevant events.
3. Now that your app has permissions, you can write some code in your Spin app to listen to events such as a reaction emoji that’s added to a message. Go back to your code editor and add the following code:
def handle_request(request):
json_data = request.body.decode('utf-8')
# Parse the JSON string
data = json.loads(json_data)
# Access the 'reaction' value
reaction_value = data['event']['reaction']
# Print the result
print(reaction_value)
# do something
The code again is straightforward. When any event occurs, there is a HTTP POST request with a JSON payload that looks something like this (I’ve redacted some tokens)
The event
parameter has the type
of event as well as the specifics, in this case the reaction
{
'token': 'XXXXXXXX',
'team_id': 'XXXXXXXX',
'context_team_id': 'XXXXXXXX',
'context_enterprise_id': None,
'api_app_id': 'XXXXXXXX',
'**event**': {
**'type': 'reaction_added',**
'user': 'XXXXXXXX',
**'reaction': 'white_check_mark',**
'item': {
'type': 'message',
'channel': 'XXXXXXXX',
'ts': 'XXXXXXXX'
},
'item_user': 'U06CRMCJW',
'event_ts': 'XXXXXXXX'
},
'type': 'event_callback',
'event_id': 'XXXXXXXX',
'event_time': 17035280,
'authorizations': [{
'enterprise_id': None,
'team_id': 'XXXXXXXX',
'user_id': 'XXXXXXXX',
'is_bot': True,
'is_enterprise_install': False
}],
'is_ext_shared_channel': False,
'event_context': '4-XXXXXXXX'
}
The Python code above just parses the JSON to extract the reaction. You can choose to do anything once you’ve received an event with the type of emoji reaction that has been added.
For example: Approve a post if someone adds a ✅ emoji.
4. Slack requires the endpoint for verification to be the same as the endpoint that is listening to any events. So we can route a request based on the payload. Just add this if
condition. Again, if you are listening to multiple events it might be better to use a switch
case or a different method of routing. Check out Rajat’s code here where he’s done the same
from spin_http import Request, Response, http_send
import json
def handle_request(request):
json_data = request.body.decode('utf-8')
data = json.loads(json_data)
<span class="c1"># Check if it's a URL verification request
if 'challenge' in data:
challenge = data.get('challenge', None)
return Response(200, {"content-type": "text/plain"}, bytes(challenge, "utf-8"))
else:
# It's an event notification
# Access the 'reaction' value
reaction_value = data['event']['reaction']
print(reaction_value)
<span class="c1"># Do something fun with the emoji reaction here
- You’re almost there! Let’s
spin build
and spin deploy
your app. Once that’s done, go to your Slack Apps and add your app to the workspace to test it out. In your app’s page, click on ‘Install App’ in the left menu and then choose the Slack Workspace and channel your bot can post to.
spin build
and spin deploy
your app. Once that’s done, go to your Slack Apps and add your app to the workspace to test it out. In your app’s page, click on ‘Install App’ in the left menu and then choose the Slack Workspace and channel your bot can post to.
6. You can double check to see if the bot has been added to the channel
To debug your code or look at the logs, you can login to [cloud.fermyon.co](http://cloud.fermyon.co)m
and click on the app that you’ve just deployed. Click on logs on the left and you will see that all your print
statements and events are logged here in real-time. You can even have a custom domain for this endpoint by clicking on the Custom Domains
section in the left menubar.
Next Steps
That was a simple tutorial on how you can create a simple webhook that listens to an emoji reaction. You can expand this to do all sorts of cool Slash commands in Slack. You can also use this with custom emojis. You’ll notice in the first screenshot at the top of this page, there’s a 🍇 emoji. That’s the reaction emoji we used (Ask us why in the comments, there’s a fun story there 😄)
Also, check out how fast the responses are! This is because Spin uses WebAssembly which has no cold-starts. This makes it ideal for Slack bots and webhooks.
*In Part II of this article, we will continue this and see what we can do when a user adds a emoji to a message. *
Let us know what type of Slack bots you are building. We’d love to hear from you <3