Building a customizable Telegram bot in Go
Telegram bots are powerful tools for automating tasks and interacting with users. They are also easy to build and deploy, so it looked like a good idea to build one to play around with Go. This post will guide you through building a customizable Telegram bot. If you’d like to see the final code or simply go and grab the ready-to-use script to customize and run, you can find on GitHub. The setup and running instructions are included through the post.
Understanding Telegram bots⌗
A bot in Telegram is a special account that is not linked to a phone number. It can be used to receive messages and send responses. The bot API is simple and well-documented. Note that there are two ways to receive updates from a Telegram chat: via the getUpdates method, or by setting a webhook. The getUpdates method is a simple way to poll for new messages, but I’d recommend to use a webhook because they are faster and more reliable. This post will focus on setting up a webhook.
Creating a bot⌗
To create a bot, you need to talk to the BotFather via Telegram. This bot will help you create and manage your bots. You can create a new bot by sending the /newbot command to the @BotFather. Follow the instructions to set up your bot and note down your API token.
Setting up the bot⌗
Create a new project folder and initialize a Go module:
$ mkdir telegram-bot
$ cd telegram-bot
$ go mod init telegram-bot
Install the required dependencies. In this case, we will use godotenv to manage environment variables through a .env file, yaml.v2 to parse the triggers, and bluemonday to sanitize the input.
$ go get github.com/lpernett/godotenv github.com/microcosm-cc/bluemonday gopkg.in/yaml.v2
Loading the configuration from .env⌗
Create a .env file in the project root with the following content:
TELEGRAM_BOT_TOKEN=your-telegram-bot-token
PORT=8080
Then, load the environment variables in the code:
err := godotenv.Load()
if err != nil {
log.Fatal("Error loading .env file")
}
if os.Getenv("TOKEN") == "" || os.Getenv("PORT") == "" {
log.Fatalf("TOKEN or PORT is missing")
}
Loading the YAML file⌗
Create a triggers.yaml file in the project root with the following content:
- key: hello
values: Hello!
- key: say hello to
values: Hello __input__, how are you?
Then, load the triggers in the code:
type Trigger struct {
Key string `yaml:"key"`
Values interface{} `yaml:"values"`
}
var triggers []Trigger
func initializeTriggers() error {
file, err := ioutil.ReadFile("./triggers.yml")
if err != nil {
return err
}
return yaml.Unmarshal(file, &triggers)
}
Handling incoming messages⌗
Telegram sends updates using JSON. We need to parse the incoming message and send a response. The message structure is defined in the Telegram API.
type Update struct {
Message Message `json:"message"`
}
type Message struct {
Chat Chat `json:"chat"`
Text string `json:"text"`
}
type Chat struct {
Id int `json:"id"`
}
Then, we can handle the incoming messages:
func callHandler(w http.ResponseWriter, r *http.Request) {
var update Update
err := json.NewDecoder(r.Body).Decode(&update)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
return
}
responseText := computeResponse(update.Message.Text)
if responseText != "" {
sendTextToChat(update.Message.Chat.Id, responseText)
}
}
Computing the response⌗
Match the incoming message with the triggers loaded from the YAML file:
func computeResponse(input string) string {
for _, trigger := range triggers {
if strings.Contains(strings.ToLower(input), strings.ToLower(trigger.Key)) {
switch values := trigger.Values.(type) {
case string:
return values
case []interface{}:
return values[rand.Intn(len(values))].(string)
}
}
}
return ""
}
Setting up the web server⌗
Listen for incoming requests using Go’s net/http package:
func main() {
loadEnvVariables()
err := initializeTriggers()
if err != nil {
log.Fatalf("Error initializing triggers: %s", err)
}
http.HandleFunc("/", callHandler)
log.Printf("Server running on port %s", os.Getenv("PORT"))
err = http.ListenAndServe(fmt.Sprintf(":%s", os.Getenv("PORT")), nil)
if err != nil {
log.Fatalf("Server error: %s", err)
}
}
Sending messages⌗
Call the Telegram bot API to send the message back to the user/chat:
func sendTextToChat(chatId int, text string) {
url := fmt.Sprintf("https://api.telegram.org/bot%s/sendMessage", os.Getenv("TOKEN"))
_, err := http.PostForm(url, url.Values{
"chat_id": {strconv.Itoa(chatId)},
"text": {text},
})
if err != nil {
log.Printf("Error sending message: %s", err)
}
}
Running⌗
To run the bot, you need to set up the webhook. You can do so using the Telegram API:
curl -X POST "https://api.telegram.org/bot<YOUR_TOKEN>/setWebhook?url=<YOUR_SERVER_URL>"
Then, run the Go script:
$ go run main.go
Conclusion⌗
Building a Telegram bot is a fun way to approach a new language or technology, since it’s pretty straightforward and you can see results quickly. This post covered the basics, but you can use it to automate tasks, send notifications, or even build a chatbot. The possibilities are endless, so go ahead and build your own!