Simple Telegram Bot With Golang

ยท 740 words ยท 4 minute read
Photo
Photo by Dima Solomin on Unsplash
Build a simple Telegram bot for sending messages to a channel or chat

Telegram is a popular messaging service available on various platforms such as Web, Android and iOS. In addition to the basic messaging, the API and Bot ecosystem make the service quite attractive for developers. The folks at telegram make it easy to play with the API and build applications to interact with the chat system. In this blog post, we will learn how to create a Telegram bot and send messages to a chat. For this to work, we need the help of the Godfa… I mean BotFather, to invite our bot into the family.

I will call upon you to do a service for me - What is a Telegram bot? ๐Ÿ”—

A Telegram bot is a program that interacts with users via chat or channel. The users in turn, can send commands to the bot. The bot will interact with the Telegram API via simple HTTP calls.

I’m gonna make him an offer he can’t refuse - Creating a new bot ๐Ÿ”—

As our first step, whe need the blessing of the BotFather to create a new bot.

The BotFather
The BotFather sends his regards

For this purpose, we need a Telegram account and access to the app on an arbitrary platform (web will do fine). Within the app, we then search for the BotFather and shoot him a message. We should introduce ourselves with /start. Creating a new bot is pretty self-explanatory, as the BotFather is actually a pretty nice guy. The process is initiated with /newbot and the BotFather will guide you through the next steps. What’s important is the token we will receive after having successfully created our bot. This token is required to authenticate the bot against the Telegram API. We should store it securely, because whoever has access to the token can control our bot.

Let’s talk business - Sending messages ๐Ÿ”—

Sending a message to a Telegram channel is nothing more than a POST request to the API. In addition to the URL, we require the chat ID and the actual text as a JSON payload, which looks like this:

{
  "chat_id": -123456789,
  "text": "My father made him an offer he couldn't refuse."
}

Now how to get the chat ID?

One possibility is to invite the bot to the channel or chat to which it is supposed to send messages. Afterwards, we can check for chat updates with the following HTTP request to see the chat ID:

https://api.telegram.org/bot<YOUR_TOKEN>/getUpdates

The response will look something like this:

{
  "ok": true,
  "result": [
    {
      "update_id": 987654,
      "channel_post": {
        "message_id": 2,
        "sender_chat": {
          "id": -123456789,
          "title": "chat_title",
          "username": "m_corleone",
          "type": "channel"
        },
        "chat": {
          "id": -123456789,
          "title": "chat_title",
          "username": "m_corleone",
          "type": "channel"
        },
        "date": 1637085387,
        "text": "My father made him an offer he couldn't refuse."
      }
    }
  ]
}

Notice the chat ID (in this example -123456789). Make sure that there is at least one message for the bot to read, otherwise you will just receive an empty json message:

{
  "ok": true,
  "result": []
}

With the chat ID, the bot token, and the Go HTTP client of the standard library, we have all ingredients to build our bot. The URL for sending a message is the following:

https://api.telegram.org/bot<YOUR_TOKEN>/sendMessage

We create the following struct to represent our message. It follows the API documentation:

// Message represents a Telegram message.
type Message struct {
	ChatID int64  `json:"chat_id"`
	Text   string `json:"text"`
}

Finally, we can send the message with the following go code:

// SendMessage sends a message to given URL. 
func SendMessage(url string, message *Message) error {
	payload, err := json.Marshal(message)
	if err != nil {
		return err
	}
	response, err := http.Post(url, "application/json", bytes.NewBuffer(payload))
	if err != nil {
		return err
	}
	defer func(body io.ReadCloser) {
		if err := body.Close(); err != nil {
			log.Println("failed to close response body")
		}
	}(response.Body)
	if response.StatusCode != http.StatusOK {
		return fmt.Errorf("failed to send successful request. Status was %q", response.Status)
	}
	return nil
}

Note that the bot has to be a member of the chat or channel and needs to have the necessary permissions to send messages.

Take the gun, leave the cannoli - Conclusion ๐Ÿ”—

The SendMessage code can easily be integrated into an HTTP server or a Lambda function to send messages on demand. The process of creating a new bot is easy and straightforward. One could integrate Telegram for instance into a build pipeline to have build results delivered via chat. Just make sure you don’t send Sicilian messages :P