1. First we create our bot using BotFather. We set the name, the username and we receive a token.

2. We test the bot registration by writing in browser’s command line:

https://api.telegram.org/bot$TELEGRAM_TOKEN/getMe

or in a terminal:

curl https://api.telegram.org/bot$TELEGRAM_TOKEN/getMe

3. We set for our bot a cool profile picture as well as an accurate description of what it does.

4. Handling user requests

This is the programming part:

// parseTelegramRequest handles incoming update from the Telegram web hook

func parseTelegramRequest(r *http.Request) (*Update, error) {
   var update Update

   if err := json.NewDecoder(r.Body).Decode(&update); err != nil {
      log.Printf("could not decode incoming update %s", err.Error())
      return nil, err
    }

   return &update, nil
}

Here we develop the features that make your bot outstanding, i.e. we implement the logic of the bot:

// HandleTelegramWebHook sends a message back to the chat with a punchline starting by the message provided by the user.

func HandleTelegramWebHook(w http.ResponseWriter, r *http.Request) { 

  // Parse incoming request
  var update, err = parseTelegramRequest(r)

  if err != nil {
    log.Printf("error parsing update, %s", err.Error())
    return
  }

  // Sanitize input
  var sanitizedSeed = sanitize(update.Message.Text) 

  // Call RapLyrics to get a punchline
  var lyric, errRapLyrics = getPunchline(sanitizedSeed)

  if errRapLyrics != nil {
     log.Printf("got error when calling RapLyrics API %s", errRapLyrics.Error())
     return
  }

 // Send the punchline back to Telegram
 var telegramResponseBody, errTelegram = sendTextToTelegramChat(update.Message.Chat.Id, lyric)

 if errTelegram != nil {
    log.Printf("got error %s from telegram, reponse body is %s", errTelegram.Error(), telegramResponseBody)
 } else {
    log.Printf("punchline %s successfuly distributed to chat id %d", lyric, update.Message.Chat.Id)
 }
}

We did some processing based on the value of the Message, and we are ready to send a Message back to the Chat. Let’s zoom on the sendTextToTelegramChat function to see how we can do this:

// sendTextToTelegramChat sends a text message to the Telegram chat identified by its chat Id

func sendTextToTelegramChat(chatId int, text string) (string, error) {

    log.Printf("Sending %s to chat_id: %d", text, chatId)

    var telegramApi string = "https://api.telegram.org/bot" + os.Getenv("TELEGRAM_BOT_TOKEN") + "/sendMessage"

    response, err := http.PostForm(
       telegramApi,
       url.Values{
          "chat_id": {strconv.Itoa(chatId)},
          "text":    {text},
     })

     if err != nil {
        log.Printf("error when posting text to the chat: %s", err.Error())
        return "", err
     }

     defer response.Body.Close() 

    var bodyBytes, errRead = ioutil.ReadAll(response.Body)

    if errRead != nil {
       log.Printf("error in parsing telegram answer %s", errRead.Error())
       return "", err
    }

    bodyString := string(bodyBytes)
    log.Printf("Body of Telegram Response: %s", bodyString)

    return bodyString, nil
}

In the 3rd line (the one with var telegramApi string) we send our message to the chat by making a POST request to the explicit /sendMessage endpoint of the Telegram API. To do this, we need to authenticate using the bot token the BotFather gave you during your bot creation.

Let’s recap:

  • We now have a handler function that does all the processing
  • Our handler decodes incoming Telegram requests,
  • Our handler can access the fields of the Update, like the Message.Text to perform some transformation, like generating rap music lyrics in the example of the RapGeniusBot.
  • Our handler ultimately POSTs a message back to the chat that initiated the conversation.

5. We do some tests

… to ensure our handler works as expected. For instance, we can test that our handler correctly parses incoming Telegram requests. This is fairly easy using the net/http/httpest package in Go. Let’s write our test in a separate test file, for example parse_update_test.go .

import (
	"bytes"
	"encoding/json"
	"net/http/httptest"
	"testing"
)

func TestParseUpdateMessageWithText(t *testing.T) {
	var msg = Message{
		Text: "hello world",
		Chat: chat,
	}

	var update = Update{
		UpdateId: 1,
		Message:  msg,
	}

	requestBody, err := json.Marshal(update)
	if err != nil {
		t.Errorf("Failed to marshal update in json, got %s", err.Error())
	}
	req := httptest.NewRequest("POST", "http://myTelegramWebHookHandler.com/secretToken", bytes.NewBuffer(requestBody))

	var updateToTest, errParse = parseTelegramRequest(req)
	if errParse != nil {
		t.Errorf("Expected a <nil> error, got %s", errParse.Error())
	}
	if *updateToTest != update {
		t.Errorf("Expected update %s, got %s", update, updateToTest)
	}

}

6. Deploy our handler

We copy all our handler code (shouldn’t forget the imports) and paste it within the Function.go file.

We compile this function. And we host it on our server.

We set the webhook handling function using the command in webbrowser:

https://api.telegram.org/bot{my_bot_token}/setWebhook?url={url_to_send_updates_to}

We can delete it using

https://api.telegram.org/bot{my_bot_token}/deleteWebhook