We shall create a Slack Bot in Go which aware of Batch Presence change event.
Essentially we are going to build and run the example given here: https://github.com/nlopes/slack/blob/master/examples/connparams/connparams.go. This tutorial give a more detailed actions of how to make it run, so that even a relatively new can follow this easily.
An examples of presence change event is when a member of the chat group changes their status from active into away.
Slack provides an update to their API for a more efficient presence change detection, by having it in a batch. This is in accordance to proliferation of groups with large members in Slack, where changes of presence could resulting in expensive handling of many events at a short period of time, or at the cost of missing some of the event.
In this tutorial we consider that a Golang has been installed. You can also refer to https://golang.org/doc/install for such purpose.
For a more clear step by step activities, we can refer to commits of https://github.com/suekto-andreas/tutorial-go/commits/TUT-001.
Out of the code created by following this tutorial, you can expand it into a more complex bot, one of the very preliminary extension can be happened at creating Handlers into the event. (This tutorial is just simply doing print.)
Not too mention once you generates Handlers and creating a more complex business process / use cases, takes care of the package oriented design, provides automated unit testing. I personally prefer the concept of treating a Go project like a library, while the executable is grouped inside cmd
directory.
First we need the token for accessing the API. Please refer to the following link to do so : https://get.slack.help/hc/en-us/articles/215770388-Create-and-regenerate-API-tokens
We are using the following library to communicate with Slack : https://github.com/nlopes/slack. Let’s go get it, run the following command in terminal
$ go get github.com/nlopes/slack
For this purpose, as we simply create a command line to be executable, I structured this as following:
this tutorial project structure
When you are aiming for building a specific slack bot project, you might prefer a less hierarchy here, as the above structure is prepared for more tutorial codes to be put under 1 repository. An example would be that the batchaware become the root project directory, instead of tutorial-go.
Our code shall be a simple main function. First of all let’s instantiate a Slack object. Replace “YOUR TOKEN HERE” with the one we created in the previous step. (If you are considering a production code, ensure that token here is provided in a better way, not hard coded, put it for example as ENVIRONMENT variable.)
package main
import ("fmt""log""net/url""os"
"github.com/nlopes/slack")
func main() {api := slack.New("YOUR TOKEN HERE",slack.OptionDebug(true),slack.OptionLog(log.New(os.Stdout, "slack-bot: ",log.Lshortfile|log.LstdFlags)),)}
Next is for us to establish an RTM (Real Time Messaging) connection with Slack. Here is we pass in the Option parameter that indicates our Bot is ready for batch presence aware.
rtm := api.NewRTM(slack.RTMOptionConnParams(url.Values{"batch_presence_aware": {"1"}}))
Let’s start the connection
go rtm.ManageConnection()
Afterward we can listen to even by looking at the IncomingEvents channel, using the following for loop
for msg := range rtm.IncomingEvents {fmt.Print("Event Received: ")}
We are going to send hello world to the channel where we want our bot to be listening to. We use the ConnectedEvent to do this, this is an internal event of the library, not a Slack event. Upon this event we SendMessage which indicates the corresponding Slack Channel ID, we want the Bot listening to.
for msg := range rtm.IncomingEvents {fmt.Print("Event Received: ")
switch ev := msg.Data.(type) {
case \*slack.ConnectedEvent:
fmt.Println("Infos:", ev.Info)
fmt.Println("Connection counter:", ev.ConnectionCount)
// Replace ABCDEFGH with your Channel ID
rtm.SendMessage(
rtm.NewOutgoingMessage("Hello world", "ABCDEFGH"))
default:
// Ignore other events..
fmt.Printf("Unexpected: %v\\n", msg.Data)
}}
To activate presence change event we need to first subscribe for this event (it’s a mandatory step since Jan 2018.) We going to subscribe during HelloEvent, which is a Slack event when the Bot has successfully connected. For this step we need USER-IDs, a simple way to retrieve some of this can be done by following this step here. (For a more automated way, the following link could inspired you.)
for msg := range rtm.IncomingEvents {fmt.Print("Event Received: ")
switch ev := msg.Data.(type) {
case \*slack.ConnectedEvent:
fmt.Println("Infos:", ev.Info)
fmt.Println("Connection counter:", ev.ConnectionCount)
// Replace ABCDEFGH with your Channel ID
rtm.SendMessage(
rtm.NewOutgoingMessage("Hello world", "ABCDEFGH"))
case \*slack.HelloEvent:
fmt.Printf("Hello: %v\\n", ev)
// Replace USER-ID-N here with your User IDs
rtm.SendMessage(rtm.NewSubscribeUserPresence(\[\]string{
"USER-ID-1",
"USER-ID-2",
}))
default:
// Ignore other events..
fmt.Printf("Unexpected: %v\\n", msg.Data)
}
}
After all is set and done, we now shall listen to our main target in this article, the presence change event !
for msg := range rtm.IncomingEvents {fmt.Print("Event Received: ")
switch ev := msg.Data.(type) {
case \*slack.ConnectedEvent:
fmt.Println("Infos:", ev.Info)
fmt.Println("Connection counter:", ev.ConnectionCount)
// Replace ABCDEFGH with your Channel ID
rtm.SendMessage(
rtm.NewOutgoingMessage("Hello world", "ABCDEFGH"))
case \*slack.HelloEvent:
fmt.Printf("Hello: %v\\n", ev)
// Replace USER-ID-N here with your User IDs
rtm.SendMessage(rtm.NewSubscribeUserPresence(\[\]string{
"USER-ID-1",
"USER-ID-2",
}))
case \*slack.PresenceChangeEvent:
fmt.Printf("Presence Change: %v\\n", ev)
default:
// Ignore other events..
fmt.Printf("Unexpected: %v\\n", msg.Data)
}
}
To run the bot (given the directory structure as is in this tutorial)
$ go run slacktut/batchaware/cmd/batchaware.go
Then the bot shall start listening into the Slack Channel. Try by making a member in the channel to change its presence (like from active to away.)
switch ev := msg.Data.(type) {case *slack.ConnectedEvent:fmt.Println("Infos:", ev.Info)fmt.Println("Connection counter:", ev.ConnectionCount)// Replace ABCDEFGH with your Channel IDrtm.SendMessage(rtm.NewOutgoingMessage("Hello world", "ABCDEFGH"))
case *slack.HelloEvent:fmt.Printf("Hello: %v\n", ev)// Replace USER-ID-N here with your User IDsrtm.SendMessage(rtm.NewSubscribeUserPresence([]string{"USER-ID-1","USER-ID-2",}))
default:// Ignore other events..fmt.Printf("Unexpected: %v\n", msg.Data)}}