Simple Telegram Bot in Go
Tags: go, http
Published: 2020-05-08 07:40:48

The adoption of chat-bots are at a peak now. Since facebook opened up its developer platform and opened the world of possibilities for chat-bots many platforms supported bots. Telegram is one of the leading platforms with bot functionality.

There is almost a bot for everything on telegram. For memes, searching movies, downloading youtube videos, dating and many more. Businesses engage with users through these bots. Crypto companies do airdrops, giveaways using these bots. Bots are literally everywhere. 


 Today we are going to checkout how to build a telegram bot  using Golang. So if you don't know Go this article is not for you. 
We are not using any telegram bot api wrapper libraries .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.

```go
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.

```go
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](http://api.icndb.com/jokes/random).


Our WebHookHandler Function looks like this
```go
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
```go
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 :

```go
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.

```go
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 

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

In the senReply Function we have sendMessageReqBody struct whose declaration is as follows :
```go
type sendMessageReqBody struct {
	ChatID int64  `json:"chat_id"`
	Text   string `json:"text"`
}
```

Check out the full code here [main.go](https://gist.github.com/incrypt0/8f1a1df63afe9b952e0e012d1fe91445)

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

then run 

```bash
ngrok http 8080

```

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

```bash
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 .!!!