loading...

Simple Telegram Bot in Go

incrypt0 profile image Krishnanand ・4 min read

Chatbots are everywhere now even small businesses have an assistant chatbot on their websites. Chatbots can reduce the need for humanitarian assistance. A chatbot which can recognize FAQ's and reply accordingly will be a lifesaver for small businesses.
In order to understand how a chatbot works telegram is an awesome platform. Yes, we all know telegram is awesome with lots of cool features. The telegram BOT API makes it possible to design any sort of bots that you can think of.
You might have seen some bots welcoming you when you join a telegram group these are group management bots designed using this very same telegram BOT API.

We are going to build one using golang. We are not using any telegram bot packages.We are using only the net/http package to have a better understanding how things work under the hood. Let's dive right in.

Getting started

First thing you have to do is to get a bot token after creating a bot using botfather bot on telegram and declare a constant.

const botToken = "BOT_TOKEN"

Let's start by creating a main.go file.

The first thing we need is the net/http package we also require the log package for logging the errors so import them and code the main function which looks like this.

func main() {
    err := http.ListenAndServe(":8080", http.HandlerFunc(webHookHandler))
    if err != nil {
        log.Fatal(err)
        return
    }
}

Yep that's it . That' how much it takes to make a simple http server in go.It listens at the port 8080 and invokes the function webHookHandler on recieving a http Request the next line is just how we handle errors in go.

Now that we have setup a server we want to create the webHookHandler function. You may think why the name webHookhandler its because we are using the webHooks. You can find more details here on Telegram bot docs.

Now lets take a look at the webHookHandler function.
The bot we are going to built reply's with a random joke when the user sends the command "/joke". For this we use a free API http://api.icndb.com/jokes/random.

Our WebHookHandler Function looks like this

func webHookHandler(rw http.ResponseWriter, req *http.Request) {

    // Create our web hook request body type instance
    body := &webHookReqBody{}

    // Decodes the incoming request into our cutom webhookreqbody type
    if err := json.NewDecoder(req.Body).Decode(body); err != nil {
        log.Printf("An error occured (webHookHandler)")
        log.Panic(err)
        return
    }

    // If the command /joke is recieved call the sendReply function
    if strings.ToLower(body.Message.Text) == "/joke" {
        err := sendReply(body.Message.Chat.ID)
        if err != nil {
            log.Panic(err)
            return
        }
    }
}

Whenever a message is received by the bot our http Server gets a request and we handle the request using this function.
You can see a webHookReqBody type which is a struct to which we decode the data received via http Request.

The webHookReqBody struct declaration :
We need to import the encoding/json package for this

type webHookReqBody struct {
    Message struct {
        Text string `json:"text"`
        Chat struct {
            ID int64 `json:"id"`
        } `json:"chat"`
    } `json:"message"`
}

Now whenever our bot recieves a message an http request is recieved by our server and that http request body is decoded into the webHookHandler struct. Then we check if the message is "/joke" if it is then we call the sendReply Function :

func sendReply(chatID int64) error {
    fmt.Println("sendReply called")

    // calls the joke fetcher fucntion and gets a random joke from the API
    text, err := jokeFetcher()
    if err != nil {
        return err
    }

    //Creates an instance of our custom sendMessageReqBody Type
    reqBody := &sendMessageReqBody{
        ChatID: chatID,
        Text:   text,
    }

    // Convert our custom type into json format
    reqBytes, err := json.Marshal(reqBody)

    if err != nil {
        return err
    }

    // Make a request to send our message using the POST method to the telegram bot API
    resp, err := http.Post(
        "https://api.telegram.org/bot"+botToken+"/"+"sendMessage",
        "application/json",
        bytes.NewBuffer(reqBytes),
    )

    if err != nil {
        return err
    }

    defer resp.Body.Close()

    if resp.StatusCode != http.StatusOK {
        return errors.New("unexpected status" + resp.Status)
    }

    return err
}

sendReply takes an argument the CHATID which is used to identify the telegram user inside the sendReply we first generate the text to be sent using the jokFetcher function which calls our API and gets a random joke for us.

func jokeFetcher() (string, error) {
    resp, err := http.Get("http://api.icndb.com/jokes/random")
    c := &joke{}
    if err != nil {
        return "", err
    }
    err = json.NewDecoder(resp.Body).Decode(c)
    return c.Value.Joke, err
}

We have joke struct into which the joke gets decoded into

type joke struct {
    Value struct {
        Joke string `json:"joke"`
    } `json:"value"`
}

In the senReply Function we have sendMessageReqBody struct whose declaration is as follows :

type sendMessageReqBody struct {
    ChatID int64  `json:"chat_id"`
    Text   string `json:"text"`
}

Check out the full code here main.go

Now in order to test our bot you should first install ngrok on your system

then run

ngrok http 8080

Now you will get a screen containing an https url copy that url and run the command

curl -F "url=YOUR_NGROK_URL_HERE" https://api.telegram.org/botYOUR_BOT_TOKEN_HERE/setWebhook

Now run your main.go file then send your bot a message "/joke". Enjoy .!!!

Discussion

pic
Editor guide