Skip to content

Working with Go

This guide shows an example of developing a simple bot in Go using only the standard library of the language.

Writting code

First, let's import some packages and define the necessary constants:

package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "log"
    "net/http"
)

const port = "4000"
const botApi = "http://localhost:7007/v1/bot-api"
const botKey = "PASTE_BOT_KEY_HERE"

Let's define a structure for the message type to encode and decode JSON correctly:

type Message struct {
    Id            string   `json:"id,omitempty"`
    ChatId        string   `json:"chatId"`
    SenderId      string   `json:"senderId,omitempty"`
    Body          string   `json:"body"`
    AttachmentIds []string `json:"attachmentIds,omitempty"`
    Timestamp     int64    `json:"timestamp,omitempty"`
}

Next, let's write a function to send messages to the server on behalf of the bot:

func sendMessage(chatId, body string) {
    jsonBody, err := json.Marshal(&Message{
        ChatId: chatId,
        Body:   body,
    })
    if err != nil {
        fmt.Printf("can't marshal json %s\n", err.Error())
    }

    req, err := http.NewRequest(
        http.MethodPost,
        botApi+"/chat/message",
        bytes.NewBuffer(jsonBody),
    )
    if err != nil {
        fmt.Printf("can't make a new request %s\n", err.Error())
    }

    req.Header.Add("Content-Type", "application/json")
    req.Header.Add("Authorization", botKey)

    resp, err := http.DefaultClient.Do(req)
    if err != nil {
        fmt.Printf("can't send message to user %s\n", err.Error())
    }
    defer resp.Body.Close()

    if resp.StatusCode >= 400 {
        fmt.Printf("got response with error code: %d\n", resp.StatusCode)
    }
}

Now let's write a custom handler for user messages, with some simple logic:

func messageHandler(msg Message) {
    reply := ""

    switch msg.Body {
    case "/start":
        reply = "Hello!\n\nLet's play with me using /ping command."
    case "/ping":
        reply = "PONG"
    default:
        reply = "Sorry, but I don't understand you."
    }

    sendMessage(msg.ChatId, reply)
}

Let's create an HTTP handler for the webhook to receive messages from users and pass them to the handler described above:

func webhookHandler(w http.ResponseWriter, r *http.Request) {
    // Accept only POST requests on /webhook path
    if r.Method != http.MethodPost {
        w.WriteHeader(http.StatusNotFound)
        return
    }

    var msg Message

    err := json.NewDecoder(r.Body).Decode(&msg)
    if err != nil {
        w.WriteHeader(http.StatusBadRequest)
        return
    }

    // Handling a user message in a separate goroutine
    go messageHandler(msg)

    w.WriteHeader(http.StatusOK)
}

Finally, let's declare the main function, which will be the entry point of our bot, where the HTTP server will be started:

func main() {
    http.HandleFunc("/webhook", webhookHandler)

    fmt.Println("Bot is running...")
    log.Fatal(http.ListenAndServe(":"+port, nil))
}

To start our bot, run this command:

go run main.go

Webhook setup

To start receiving messages from users, you need to connect a webhook for our bot. We have launched the bot on a local network on a port 4000, so our webhook will look like this:

curl --request POST \
  --url http://localhost:7007/v1/bot-api/bot/webhook \
  --header 'Authorization: PASTE_BOT_TOKEN_HERE' \
  --header 'Content-Type: application/json' \
  --data '{"url": "http://localhost:4000/webhook"}'

Note that if you running Botyard using Docker, accessing to localhost from the container will not work. Watch this video to solve the problem.

See the full bot API reference.