Build a Slack Emoji Bot in 20 Lines of Code
Tags: python, beginners, cloud, serverless
Published: 2024-01-26 20:56:29

My colleague Rajat recently built a [Behind The Scenes](https://fermyon-bts.usingspin.com/) 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](https://www.fermyon.com/blog/social-photo-app-using-spin) 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. 


![The Behind the Scenes app in action](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kpnjq3ow2b8thlqfk52x.png)



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](http://api.slack.com) and click on ‘Your Apps’


![Slack's configuration](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vkmxi283yrxbchufu1er.png)



#### 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 🙃


![Find "Your Apps"](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/t2ynb9adruebhdzg9twe.png)




![Create a new app](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gzsaa4ybd4xcquigi6m9.png)



#### 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


![Add basic information](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m22q2t7qfcdeqxirvxhe.png)



#### 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:

```flow
{
    "token": "Jhj5dZrVaK7ZwHHjRyZWjbDl",
    "challenge": "3eZbrw1aBm2rZgRNFdxV2595E9CY3gmdALWMmHkvFXO7tYXAYM8P",
    "type": "url_verification"
}
```

[See the documentation here for more info](https://api.slack.com/events/url_verification). 


![Event Subscriptions](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sufovh4ec3zr64f5dzyv.png)



Hold on, I need a backend endpoint to verify the Events API and then <do something> 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](https://developer.fermyon.com/spin/v2/quickstart). 

#### 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:

```bash
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: /...
```

```bash
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` 

```python
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](https://api.slack.com/docs/verifying-requests-from-slack) and then respond in plaintext or with `application/x-www-form-urlencoded` or even in JSON. 
2. 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 a `npm install` before to install some dependancies. 
3. 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)

```bash
> 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. 

```bash
chat:write, im:history, incoming-webhook, reactions:read
```


![Scopes](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mfb14h5zwqna0mi4j1mu.png)



You also need to subscribe to bot events. In the ‘Event Subscriptions’ section, scroll down to ‘Subscribe to bot events’ and add the following. 

```bash
message.im, reaction_added
```

Again, if you expand the scope of your bot, ensure you add the relevant events. 


![Subscribe to Bot Events](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wjkkrdw1y9xi1o8615nz.png)



#### 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:

```python
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`

```python
{
	'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](https://github.com/rajatjindal/behind-the-scenes/blob/main/api/pkg/webhook/webhook.go#L77) where he’s done the same

```python
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)

    # 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)

        # Do something fun with the emoji reaction here
```

#### 5. 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. 

![Choose a channel](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/61jo1foebl1wy3zhwm71.png)



#### 6. You can double check to see if the bot has been added to the channel 


![Reaction emoji was added](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hna6x3cha37bg2dk7zlx.png)


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