Creating a Go module

We’re going to create a CLI tool for sending a message to a channel in Slack using the command line. This post is similar to my earlier post: Creating an Elixir Module. We’ll be using the chat.postMessage Slack API endpoint. Also, make sure you have a Slack API token.

Our CLI syntax will be:

$ ./slack -message 'hello world!' -channel @slackbot

First, make sure you have your $GOPATH set properly.

export GOPATH="/path/to/go/"

Next, set your Slack token as an environment variable:

export SLACK_TOKEN=<token>

Typically, developers will use one folder for all of their Go code, and create new folders within for each new project. My structure looks like this:

go β”œβ”€β”€ bin β”œβ”€β”€ pkg └── src

Make a folder called slack within src, then inside that folder create the following files and folders as well:

slack/ β”œβ”€β”€ api β”‚   └── slack.go └── main.go

Using the builtin Go command line parser flag, we will write our main.go file to parse the message and channel from the command line.

slack/main.go

package main import ( "slack/api" "flag" ) func main() { msg := flag.String("message", "", "The `message` to send the `channel`") channel := flag.String("channel", "", "The `channel` to send the `message`") flag.Parse() api.SendMsg(*msg, *channel) }

This sets us up with a CLI parser which will take our provided arguments and pass them to the SendMsg function, which we will define now.

Make sure you have set the environment variable SLACK_TOKEN with your token.

slack/api/slack.go

package api import ( "fmt" "net/http" "net/url" "os" ) const slackUrl string = "https://slack.com/api/" const chatPostMsg string = "chat.postMessage" func SendMsg(msg, channel string) { token := getToken() // keys and values for query string parameters used in the API call paramMap := map[string]string{ "token": token, "as_user": "true", "text": msg, "channel": channel, } url := buildUrl(slackUrl+chatPostMsg, paramMap) resp, err := http.Post(url, "application/json", nil) // handle and error calling `http.Post` if err != nil { fmt.Println(err) fmt.Println("failed to post") } // handle non-200 status codes if resp.StatusCode == 200 { fmt.Println("Sent message!") fmt.Println(fmt.Sprintf("%s <- %s", channel, msg)) } else { fmt.Println("Failed to send message.") fmt.Println(resp) } } // Build the URL using the parameters map and builtin URL library func buildUrl(urlString string, args map[string] string) string { u, err := url.Parse(urlString) if err != nil { fmt.Println("Error parsing URL string") } v := url.Values{} for key, value := range args { v.Set(key, value) } u.RawQuery = v.Encode() return u.String() } // check that token is set in environment func getToken() string { token := os.Getenv("SLACK_TOKEN") if token == "" { fmt.Println("Environment variable `SLACK_TOKEN` not set") fmt.Println("Set with:") fmt.Println("\texport SLACK_TOKEN=<token>") panic("Exiting") } return token }

With these two files written, we can go the to slack folder and test the program:

$ go run main.go -channel @slackbot -message 'hello!'

If all goes well, you will see the output:

Sent message! @slackbot <- hello!

and the message will show up from you to slackbot in the Slack app.

To build the program as a standalone, distributable binary run:

$ go build -o slack

This creates a binary file called slack in the folder, which can be run with:

$ ./slack -message 'hello world!' -channel @slackbot

That’s it!

Thanks to Hans Li for the help with testing!


Contents