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:

    ├── bin
    ├── pkg
    └── src

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

    ├── 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.


package main

import (

func main() {
    msg := flag.String("message", "", "The `message` to send the `channel`")
    channel := flag.String("channel", "", "The `channel` to send the `message`")
    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.


package api

import (

const slackUrl string = ""
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("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.")

// 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>")
    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!