Develop a Slack-bot using Golang
Learn how to build a Slack bot in Golang with this step-by-step tutorial
by Percy Bolmér, August 13, 2021
Slack is a communication tool used by developers and companies to share information and communicate. It has grown very popular in recent years.
In this article, we will cover how to set up and build a bot that can interact with the Slack workspace and channels. We will look into how to create slash commands and visualized requests such as buttons. The bot application will be sending requests to a Go backend via Websocket, something called Socket-Mode in the slack world.
Create the Slack workspace
If you don’t already have a workspace to use, make sure to create a new one by visiting slack and press Create a new Workspace
.
Go ahead and fill all the forms, you will need to provide a name for the team or company, a new channel name, and eventually invite other teammates.
Creating the Slack Application
The first thing we need to do is to create the Slack application. Visit the slack website to create the application. Select the From scratch
option.
You will be presented with the option to add a Name to the application and the Workspace to allow the application to be used. You should be able to see all workspaces that you are connected to. Select the appropriate workspace.
There are many different use cases for an Application. You will be asked to select what features to add, we will create a bot so select the Bot option.
After clicking Bots you will be redirected to a Help information page, select the option to add scopes. The first thing we need to add to the application is the actual permissions to perform anything.
After pressing Review Scopes to Add
, scroll down to Bot scopes and start adding the 4 scopes I’ve added. The explanation of the scopes is present in the image.
After adding the scopes we are ready to install the application. If you’re the owner of the application you can simply install it, otherwise, like in my case, I have to request permission from an Admin.
If you can Install or are allowed to install you will see yet another screen with information, select the appropriate channels the bot can use to post on as an application.
Once you click Allow
you will see long strings, one OAuth token, and one Webhook URL. Remember the location of these, or save them on another safe storage.
Open your slack client and log in to the workspace. We need to invite the Application into a channel that we want him to be available in. I’ve used a channel named percybot
.
Go there and start typing a command message which is done by starting the message with /
. We can invite the bot by typing /invite @NameOfYourbot.
Connecting to Slack from Golang
Now that we have the Slack application up and the authentication token we can start communicating with the Slack channel.
We will be using goslack which is a library that supports the regular REST API, WebSockets, RTM, and Events. We will also use godotenv to read environment variables.
Let’s create a new golang package and download it.
mkdir slack-bot
cd slack-bot
go mod init programmingpercy/slack-bot
go get -u github.com/slack-go/slack
go get -u github.com/joho/godotenv
First of all, we will create a .env
file that can be used to store your secret token. We will also store a channel ID here. You can find the Token in the web UI where you created the application, the channel can be found in the UI if you select the channel and go to Get channel details
by pressing the carrot arrow.
Create main.go
so we can start coding. We will begin by simply connecting to the workspace and posting a simple message to make sure everything is working.
We will use godotenv
to read in the .env
file. Then create a Slack Attachment, which is a message that is sent to the channel. What’s important to understand is that the Slack package leverages a pattern where most functions take a Configuration slice. What this means is that there are Option
functions that can be added in each request, and a variable amount.
We will also add some Fields to the message which can be used to send extra contextual data.
Execute the program by running the main function, you should then see a new message in the slack channel.
go run main.go
Using the Slack Events API
The slack events API is a way to handle events that occur in the Slack channels. There are many events, but for our bot, we want to listen to the mentions event. This means that whenever somebody mentions the bot it will receive an Event to trigger on. The events are delivered via WebSocket.
You can find all the event types available in the documentation.
The first thing you need to do is attend your Application in the web UI.
We will activate something called Socket Mode
, this allows the bot to connect via WebSocket. The alternative is to have the bot host a public endpoint, but then you need a domain to host on.
Then we also need to add Event Subscriptions
. You can find it in the Features tab, enter it, and activate it. Then add the app_mentions
scope to the Event subscriptions. This will make mentions trigger a new event to the application
The final thing we need to do is generate an Application token. Right now we only have a Bot token, but for the Events, we need an Application token.
Go into Settings->Basic Information
and scroll down to the chapter called App-Level Tokens and press Generate Tokens and Scope
and fill in a name for your Token.
I’ve added the connections:write
scope to that token, make sure you save the Token as well by adding it to the .env
file as SLACK_APP_TOKEN
.
To use Socket Mode we also need to get a sub package of slack-go
which is called socket mode.
go get github.com/slack-go/slack/socketmode
The slack package will need to create a new Client for the socket mode, so we will have two clients. One that uses the regular API and one for the websocket events. Let’s begin by connecting to make sure all permissions are correct. Notice how the Websocket client is created by calling socketmode.New
and given the regular client as input. I’ve also added a OptionAppLevelToken
to the creation of the regular client since that is now needed to connect to the Socket.
Make sure to run the program and verify the output that is connected, there will be a ping hello sent.
It’s time to start selecting all events to listen for. At the end of the program, we call socketClient.Run()
which will be blocking and ingesting new Websocket messages on a channel at socketClient.Events
. So we can use a for loop to continuously wait for new events, also the slack-go library comes with predefined Event types, so we can use a type switch to handle different types of Events easily. All events can be found here.
Since socketClient.Run()
is blocking, we will spawn a goroutine that handles incoming messages in the background.
We will begin by simply logging in whenever an Event on the EventAPI is triggered in Slack. Since we first need to type switch the message on the websocket if it’s an EventsAPI
type, then switch again based on the actual Event that occurred we will break the Event handling out into a separate function to avoid deeply nested switches.
If you want to test it, run the program and then enter Slack and mention by bot by using @yourbotname
.
go run main.go
You should be able to see the Event being logged in the command line running the bot.
Look at the Event printed and you will understand why we need to use multiple type switches. The event we get is of the type event_callback
, and that event contains a payload
with the actual event that was performed.
So first we need to test if it’s a Callback event and then if it’s an app_mention
payload event.
Let’s implement the handleEventMessage
that will continue the type switching. We can use the type
field to know how to handle the Event. Then we can reach the payload event by using the InnerEvent
field.
Replace the previous log in the main function that printed the event with the new handleEventMessage
function instead.
Now logging the event does not make a fun bot. We should make the bot respond to the user who mentioned him and if they said hello it should also greet them.
Begin by logging into the application and adding the users:read
scope to the bot token. I trust you to make that without guidance now, or go back and read how we did before.
Once that’s done we will create the handleAppMentionEvent
function. This function will take a *slackevents.AppMentionEvent
and a slack.Client
as input so it can respond.
The event does contain the user ID in the event.User
so we can use that ID to grab user information. The channel to respond to is also available in the event.Channel
. The final piece of information we need is the actual message the user sent when mentioning, which is found in the event.Text
.
To begin using this function we need to add the Client as an input parameter as well. So we have to update handleEventMessage
to accept it.
Restart the program and try saying Hello and also saying something else to see that it works as expected. If you get a “missing_scope” error you have missed some scope.
Here is the output of my currently running bot
It’s time to move forward and looking at how to add Slash commands.
Adding slash commands to the Slack bot
Most commonly I’ve seen slash commands being used by Slack bots. What this means is you can type /send a special command. There are many built-in commands such as /call
which allows you to start a call etc.
We will be adding a custom command which will be /hello
. When this command is triggered we will make the bot send a greetings message.
Again, you need to add the command in the web UI. Visit the website and select the Slash Command
in the features tab.
We will make a command that accepts a single parameter which is the username to greet. Fill in the fields asked for, note that we are using socket-mode so we don’t need to provide a request URL.
Don’t forget to reinstall the application after we have added the command. This is needed since we have changed the application. If you have forgotten how then revisit the earlier part of the article where we installed the application.
You can verify that everything has been installed by opening up the slack and the channel where the application is invited and type the /hello
command.
That was easy enough, let’s also redo what we did with the EventsAPI, but this time we will add a type switch for EventTypeSlashCommand
.
We will find the command called in the SlashCommand.Command
and the input text in SlashCommand.Text
. So we will first route the command based on the input of the command and then return greetings to the text field.
Begin by updating the main.go
file to include the listener for the new type of message events on the websocket.
Do not forget to send the Acknowledgement, or else you will see an error message in slack that the message was not dispatched properly.
We will have a router function called handleSlashCommand
which will simply redirect to another function. This might seem overkill for now, but if you plan to add more functions it’s easier to make multiple small functions. Especially if you are using unit tests.
The actual response will come from handleHelloCommand
which will simply take the username set after the /hello
command and send a greeting in the channel.
Restart the program and try sending a command from the slack client. When I input /hello reader
I see the following output.
Advance slash commands and interactions
We will look at how we can implement a slash command which triggers the bot to ask a question that we can answer with a Yes and No button.
Begin by adding the new command in the Slack web UI so we can trigger it.
We will make a small change to the main function first, In the type switch that accepts the Slash commands events, we currently acknowledge before we process the message. We will change this and return the response in the Acknowledge since this is possible.
Now you will see how easily we can add new commands, all we need to do is add a new case option in handleSlashCommand
to check for. Of course, we need to handle the actual command as well, but the structure is easily scaled. We will update handleSlashCommand
so that it returns an interface{}
also. This is the payload response that will be included in the acknowledgment.
We will route to a function called handleIsArticleGood
that will trigger a two-button questionnaire to the user using something called Block-Kit. It’s a Slack implementation that allows us to send HTML components. There are a ton of options and components to send, but let’s stick to buttons for now.
The blocks are added to the slack.Attachment
that we used previously to send simple messages. It has a field called Blocks
which accepts an array of blocks to send. Each block is a visual component to send.
We will be using a Section block, and the Slack library helps us create one using the NewSectionBlock()
which will accept a few parameters.
The first parameter is a slack.TextBlockObject
which is a standard way of sending text contains the type to use, in which we will use markdown. It also contains the value to display in the text block.
The second parameter is fields to add, such as we used before to add contextual data, let’s leave it as nil.
The third parameter is a slack.Accessory
which a container for a block element, you can find the JSON layout in the slack documentation. We will add a Checkbox element to the Accessory, which contains two options, [Yes, No]. Remember that we simply return the response, in this case, we don’t send it as in the hello handler. Notice the answer
in the CheckBoxGroupsBlockElement, this is the action that is used to identify what kind of interaction was performed.
Restart your bot and try executing the command in slack.
When you select something nothing will happen since we don’t accept the response in the backend yet. The response is will trigger an Interaction
Event, so if we want to accept the response we need to listen for this event in the main function.
The process is the same as before, typecast into the correct message type. In this case, it’s an InteractionCallback
.
We will add a handleInteractionEvent
that will simply print information about the interaction and the selected option.
Try executing the command, and select an option.
Conclusion
We have covered most of the items needed to get started with building your bot.
We have covered these topics
- How to set up a Slack-Bot application
- Connecting to the application from a Golang service
- Listening for bot mentions in a channel
- Adding slash commands to the bot
- Using the Slack Events API
- Sending visualized blocks to Slack
- Listening for User interactions
This is it for this time, hopefully you have enjoyed the article. As always feel free to reach out and give me feedback or questions.
Now go out there and build some bots!
If you enjoyed my writing, please support future articles by buying me an Coffee