Go telegram bot template

Good afternoon, today I will share with you, in my opinion, a rather successful template for telegram bots on go

It contains most of the popular work scenarios and its expansion should not cause problems.

Template functionality

  • Executing requests in Goroutines

  • Executing commands from the console

  • Storing context at runtime

  • Display keyboards and messages with localization

  • Return to previous menu

For those who can’t wait to take a look, welcome to GitHub
https://github.com/AnderKot/Go-TG-Bot-Template

I ask you to forgive me in advance if I get confused by the style of the code; I created the template on the 2nd day of learning the language and have not yet had time to absorb its spirit.


The template is based on the idea of ​​a unified message processing system architecture, where functionality can be added in the form of modules.

All possible actions that a bot can perform are enclosed in one abstract function Run (Run go! Run!).
A CallStack structure was created to store the launch context of these functions.

type Run func(CallStack) CallStack
type CallStack struct {
	ChatID  int64
	Bot     *tgBotAPI.BotAPI
	Update  *tgBotAPI.Update
	Action  Run
	IsPrint bool
	Parent  *CallStack
	Data    string
}

var userRuns = map[int64]CallStack{}

stack := userRuns[ID]
  if stack.Action != nil {
      stack.Update = &update
      userRuns[ID] = userRuns[ID].Action(stack)
  } else {
      if update.Message != nil {
          userRuns[ID] = RunTemplate(CallStack{
              ChatID:  ID,
              Bot:     bot,
              Update:  &update,
              IsPrint: true,
          })
      }
  }

BotLoop implements a ready-made message processing loop.

To separate stacks of execution contexts, map is used, where the key is the chat ID, but you can choose your own, for example, a user login.

If the repository has a ready-made context stack, we pass the update to it and run Run, which will return the context for the next processing.
If the action is not specified, we return the user to the starting point.

RunTemplate is a Run implementation template, it contains 3 things at once

  • Displaying interest for the user

  • Message processing

  • Initializing a new context

This makes it a self-contained bot module that can be transferred from project to project.

Then the narrative goes into code

Это общий шаблон для всех Run
func RunTemplate(stack CallStack) CallStack {
    Когда Run получает свой контекст он выставляет в нём себя в качетве
    выполняемого действия.
    Далее практически всё происходит на его основе контекста
	stack.Action = RunTemplate
    
    Информация о пользователе
	data := userDatas[stack.ChatID]

	if stack.IsPrint {
        Если нужно что-то передать пользователю пишем это тут
		
        Если передачу нужно повторять при повсторном заходе в Run 
        Уберите это снятие флага, но обработку сообщений тогда нужно будет вынести из else
        stack.IsPrint = false
        
		Тут пример вывода локализованного форматированого сообщения с прикреплёнными кнопками 
		Не пишите текст прямо в Run, заносите шаблоны для вывода в MessageTemplates
        msg := tgBotAPI.NewMessage(stack.ChatID, fmt.Sprintf(SelectTemplate("RunTemplate", data.languageСode),
			data.firstName,
		))
		mainMenuInlineKeyboard := tgBotAPI.NewInlineKeyboardMarkup(
			tgBotAPI.NewInlineKeyboardRow(
				tgBotAPI.NewInlineKeyboardButtonData(SelectTemplate("back", data.languageСode), "back"),
			),
		)
		msg.ReplyMarkup = mainMenuInlineKeyboard
		_, _ = stack.Bot.Send(msg)

        Тут отчищается прошлый набор кнопок
        Также можно задать свой набор для отображания
		mainMenuKeyboard := tgBotAPI.NewRemoveKeyboard(true)
		msg = tgBotAPI.NewMessage(stack.ChatID, "")
		msg.ReplyMarkup = mainMenuKeyboard
		_, _ = stack.Bot.Send(msg)

        И так Run подготовил интерфейс и возвратил контекст выполнения для его сохранения
        Исходя из этого, следующеt сообщение пользователя будет обработанно в этом же Run
		return stack
	} else {
        И так, пользователь ввел сообщение, вызвал комманду или другим образом попытался сломать бота )
		
        Сообщения
        if stack.Update.Message != nil {
			switch stack.Update.Message.Text {
			case "back":
				{
					return ReturnOnParent(stack)
				}
			}
		}
      
        Кнопки с inline
		if stack.Update != nil {
			// Processing a message
			if stack.Update.CallbackQuery != nil {
				switch stack.Update.CallbackQuery.Data {
				case "back":
					{
						stack.Data = stack.Update.CallbackQuery.Data
						return ReturnOnParent(stack)
					}
				}
			}
		}
      
        Комманды
        if update.Message.IsCommand() {
          switch update.Message.Command() {
          case "back":
              {
                    return ReturnOnParent(stack)
              }
          }
        }
        и т.д.
	}
    
	Если произошло что-то не предвиденное и код пришел сюда
    можно просто отправить этот же контекст для следующей обработки 
    return stack

    Или применить Затычку
    return Chop(stack)
}


func Chop(stack CallStack) CallStack {
    Затычка выведет сообшение ввиде картинки, которая сообщает что
    пользователь достиг не проработанной конечной точки
	photo := tgBotAPI.NewPhoto(stack.ChatID, chopFile)
	photo.ReplyMarkup = tgBotAPI.NewRemoveKeyboard(true)
	_, _ = stack.Bot.Send(photo)
    И попытается вернуть пользователя назад
	return ReturnOnParent(stack)
}

func ReturnOnParent(stack CallStack) CallStack {
    Это или предыдущий Run из стека
	if stack.Parent != nil {
		stack.Parent.IsPrint = true
		return *stack.Parent
	}
    Или начальная точка
	return RunTemplate(CallStack{
		IsPrint: true,
		ChatID:  stack.ChatID,
		Bot:     stack.Bot,
	})
}

This is a fairly flexible architecture; by changing Run you can achieve many interesting effects.

  • Input menu with information returning to the previous Run

  • Run Sequence Passthrough

  • Saving the context stack when stopped and restored when turned on

  • If you add reflection, you can make the bot completely configurable with external files

  • Changing the execution sequence of Runs during operation

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *