Skip to content

A template for telegram bots in go

Notifications You must be signed in to change notification settings

w1png/telegram-bot-template-go

Repository files navigation

English

Language

Creating a language string

Open language/language_string.go file. Add your string to the list of constants.

package language

type LanguageString string

const (
	...
    Greet LanguageString = "Greet"
    ...
)

Open all the language files i.e. language/language_en.go and add your string to the map.

package language

import "github.com/w1png/telegram-bot-template/errors"

type English struct {
	Values map[LanguageString]string
}

func NewEnglish() *English {
	return &English{
		Values: map[LanguageString]string{
			...
            Greet: "Hello!",
            ...
		},
	}
}

func (e *English) Get(key LanguageString) (string, error) {
	value, ok := e.Values[key]
	if !ok {
		return "", errors.NewLanguageStringError(string(key))
	}
	return value, nil
}

Getting your language string

import "github.com/w1png/telegram-bot-template/language"

func printGreet() {
	s, err := language.LanguageInstance.Get(language.Greet)
	if err != nil {
		return log.Fatal(err)
	}
    log.Println(s)
}

Creating a command handler

Create a file in handlers/commands/ folder i.e. handlers/commands/greet.go.

package commands

import (
	tg "github.com/go-telegram-bot-api/telegram-bot-api/v5"
	"github.com/w1png/telegram-bot-template/models"
	"github.com/w1png/telegram-bot-template/types"
)

func GreetCommand() types.UpdateHandlerFunction {
	return func(bot *tg.BotAPI, update tg.Update, user *models.User) (tg.Chattable, error) {
        s, err := language.LanguageInstance.Get(language.Greet)
        if err != nil {
            return nil, err
        }

		return tg.NewMessage(
			update.FromChat().ID,
            s,
		), nil
    }
}

Open handlers/commands/commands.go and add your command to the map.

package commands

import "github.com/w1png/telegram-bot-template/types"

var CommandsMap = map[string]func() types.UpdateHandlerFunction{
    "greet": GreetCommand,
}

You can now use your /greet command.

greet

Creating a message handler

Message handlers are useful for handling keyboard button inputs.

For example, let's create a /start command that will send a greeting message with a keyboard.

func StartCommand() types.UpdateHandlerFunction {
	return func(bot *tg.BotAPI, update tg.Update, user *models.User) (tg.Chattable, error) {
		text, err := language.LanguageInstance.Get(language.Start)
		if err != nil {
			return tg.MessageConfig{}, err
		}

		replyMsg := tg.NewMessage(update.Message.Chat.ID, text)
		replyMsg.ReplyToMessageID = update.Message.MessageID
		replyMsg.ReplyMarkup = tg.NewReplyKeyboard(
			tg.NewKeyboardButtonRow(
				tg.NewKeyboardButton("Help"),
			),
		)

		return replyMsg, nil
	}
}

help_start

Create a file in handlers/messages/ folder i.e. handlers/messages/help.go.

package messages

import (
	tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
	"github.com/w1png/telegram-bot-template/models"
	"github.com/w1png/telegram-bot-template/types"
)

func HelpMessage() types.UpdateHandlerFunction {
	return func(bot *tgbotapi.BotAPI, update tgbotapi.Update, user *models.User) (tgbotapi.Chattable, error) {
		return tgbotapi.NewMessage(update.Message.Chat.ID, "This is a help message"), nil
	}
}

Open handlers/messages/messages.go and add your message handler to the map.

package messages

import "github.com/w1png/telegram-bot-template/types"

var MessagesMap = map[string]func() types.UpdateHandlerFunction{
    ...
    "help": HelpMessage,
    ...
}

You can now use your Help button.

help

Callbacks

Marshaling callback data

Callback data has a call (a key in the CallbackMap) and data that can be anything.

type CallbackData struct {
	Call string `json:"c"`
	Data any    `json:"d"`
}

If you, for example, you are making an ecommerce bot and you want to make a button that opens the category with id 1.

...
callback_data, err := types.NewCallbackData("category", "1").Marshal()
...

The button callback data will look like this: {"c":"category","cid":1}

You can pass any JSON serializable data to the callback data. This can be usefull for creating a back button.

...
callback_data, err := types.NewCallbackData(
  "category",
  struct {
    CategoryId uint `json:"cid"`
    OpenedFromCategoryId  uint `json:"from"`
  }{
    CategoryId: 1,
    OpenedFromCategoryId: 2,
  },
).Marshal()
...

The button callback data will look like this: {"c":"category",{"cid":1,"from":2}}

It's a great idea to use short strings for keys and data because telegram has a character limit for button callback data.

Creating a callback

For example, let's create a /start command that will send a greeting message with an inline keyboard.

func StartCommand() types.UpdateHandlerFunction {
	return func(bot *tg.BotAPI, update tg.Update, user *models.User) (tg.Chattable, error) {
		text, err := language.LanguageInstance.Get(language.Start)
		if err != nil {
			return tg.MessageConfig{}, err
		}

		callback_data, err := types.NewCallbackData("category", "1").Marshal()
		if err != nil {
			return nil, err
		}

		replyMsg := tg.NewMessage(update.Message.Chat.ID, text)
		replyMsg.ReplyToMessageID = update.Message.MessageID
		replyMsg.ReplyMarkup = tg.NewInlineKeyboardMarkup(
			tg.NewInlineKeyboardRow(
				tg.NewInlineKeyboardButtonData("Open category", string(callback_data)),
			),
		)

		return replyMsg, nil
	}
}

2024-01-08 2 32 16 PM

Create a file in handlers/callbacks/ folder i.e. handlers/callbacks/category.go.

package callbacks

import (
	"fmt"

	tg "github.com/go-telegram-bot-api/telegram-bot-api/v5"
	"github.com/w1png/telegram-bot-template/language"
	"github.com/w1png/telegram-bot-template/models"
	"github.com/w1png/telegram-bot-template/storage"
	"github.com/w1png/telegram-bot-template/types"
)

func CategoryCallback(data types.CallbackData) types.UpdateHandlerFunction {
	return func(bot *tg.BotAPI, update tg.Update, user *models.User) (tg.Chattable, error) {
		category_id, ok := data.Data.(uint)
		if !ok {
			return nil, fmt.Errorf("category_id is not string")
		}

		category, err := storage.StorageInstance.GetCategoryById(category_id)
		if err != nil {
			return nil, err
		}

		t, err := language.LanguageInstance.Get(language.CategoryFormat)
		if err != nil {
			return nil, err
		}

		return tg.NewEditMessageText(
			update.CallbackQuery.From.ID,
			update.CallbackQuery.Message.MessageID,
			fmt.Sprintf(t, category.Name),
		), nil
	}
}

Open handlers/callbacks/callbacks.go and add your callback to the map.

package callbacks

import "github.com/w1png/telegram-bot-template/types"

var CallbackMap = map[string]func(types.CallbackData) types.UpdateHandlerFunction{
    "category": CategoryCallback,
}

You can now use your Open category button.

2024-01-08 2 45 46 PM

Environment variables

Environment variables can be added to config/config.go.

Name Description
TELEGRAM_TOKEN Your Telegram bot token
IS_DEBUG Sets tgbotapi.Bot.Debug=true if IS_DEBUG=true
LANGUAGE Sets the language. Possible values are en or ru
STORAGE_TYPE Database type. Possible value is sqlite
SQLITE_PATH A path to your sqlite database. Example: data.db
LOGGER_TYPE Logger type. Possible value is console. Defaults to no logger if not set.

Development

To start the bot in live reload mode, run the air command.

Building and running

To build the docker container, run make build-docker. To the run the container, run make run-docker

About

A template for telegram bots in go

Resources

Stars

Watchers

Forks

Languages