Библиотека, вдохновлённая библиотекой telebot. Библиотека авторов Max max-bot-api-client-go имеет фатальный недостаток — её писал не я. Не для продакшна (not production ready). Пока что не оттестированно и не дописано.
- 📖 Официальная документация MAX API
- 🏛️ Официальная библиотека MAX
- 🤖 telebot - вдохновение для этой библиотеки
- 🚀 Простой и понятный API
- 🔄 Long Polling и Webhook
- 🎨 Inline клавиатуры
- 📁 Отправка файлов (фото, видео, аудио, документы)
- 🛡️ Middleware система
- 🧯 Паника в хендлере не роняет бота
- 👥 Поддержка групповых чатов
- 📊 Встроенные метрики
- ⚡ Готовые middleware (rate limiting, whitelist, и др.)
go get github.com/demen1n/maxbotpackage main
import (
"log"
"os"
"github.com/demen1n/maxbot"
)
func main() {
b, err := maxbot.NewBot(maxbot.Settings{
Token: os.Getenv("MAX_BOT_TOKEN"),
})
if err != nil {
log.Fatal(err)
}
b.Handle("/start", func(c maxbot.Context) error {
return c.Send("👋 Привет!")
})
b.Start()
}b, err := maxbot.NewBot(maxbot.Settings{
Token: "your-bot-token",
URL: maxbot.DefaultAPIURL, // опционально
Logger: log.Default(), // опционально
Poller: &maxbot.LongPoller{ // опционально
Timeout: 30 * time.Second, // до 90с по спеке; HTTP-клиент сам подстраивает свой таймаут под это значение
},
OnError: func(err error, c maxbot.Context) {
// глобальный обработчик ошибок — сюда же попадают панические
// ситуации в хендлерах (см. "Обработка ошибок и паник" ниже)
},
})// Простая команда
b.Handle("/start", func(c maxbot.Context) error {
return c.Send("Привет!")
})
// Команда с аргументами
b.Handle("/echo", func(c maxbot.Context) error {
return c.Send(c.Payload())
})
// Получение аргументов как слайс
b.Handle("/user", func(c maxbot.Context) error {
args := c.Args() // /user John Doe -> ["John", "Doe"]
if len(args) == 0 {
return c.Send("Укажите имя пользователя")
}
return c.Send("Привет, " + args[0])
})b.Handle("/menu", func(c maxbot.Context) error {
menu := &maxbot.ReplyMarkup{}
// Добавляем кнопки построчно
menu.Row(
menu.Data("Кнопка 1", "btn1"),
menu.Data("Кнопка 2", "btn2"),
)
menu.Row(
menu.URL("Открыть сайт", "https://example.com"),
)
return c.Send("Выберите действие:", menu)
})
// Обработка нажатий — сохраните кнопку в переменную перед передачей в Handle
btn1 := menu.Data("Кнопка 1", "btn1")
b.Handle(&btn1, func(c maxbot.Context) error {
return c.Send("Вы нажали кнопку 1")
})menu := &maxbot.ReplyMarkup{}
// Callback — отправляет payload боту
menu.Row(menu.Data("Нажми меня", "my_action"))
// Ссылка
menu.Row(menu.URL("Открыть сайт", "https://example.com"))
// Запрос контакта / геолокации
menu.Row(menu.Contact("Поделиться номером"))
menu.Row(menu.Geolocation("Поделиться локацией", false))
// Мини-приложение
menu.Row(menu.OpenApp("Открыть", "https://app.example.com", "deeplink", 0))
// Clipboard — копирует текст в буфер обмена
menu.Row(menu.Clipboard("Скопировать", "текст для копирования"))
// Создание чата
menu.Row(menu.Chat("Создать группу", "Моя группа", "Описание", "start"))
// Кнопка отправки шаблонного сообщения
menu.Row(menu.MessageBtn("Отправить"))В отличие от inline-клавиатуры (кнопки под сообщением), reply-клавиатура показывается рядом с полем ввода.
kb := &maxbot.ReplyKeyboard{}
kb.Row(kb.Message("Да", "yes"), kb.Message("Нет", "no"))
kb.Row(kb.Contact("Поделиться номером"))
kb.Row(kb.Geolocation("Поделиться локацией", false))
b.Send(chat, "Выберите вариант", kb)
// Ограничить клавиатуру одним участником чата
kb.Direct = true
kb.DirectUserID = userIDФайлы сначала загружаются на серверы MAX, затем отправляются сообщением.
// Фото
tokens, err := b.UploadPhoto("photo.jpg", fileData)
if err != nil {
return err
}
b.Send(chat, &maxbot.Photo{PhotoTokens: *tokens}, &maxbot.SendOptions{
Text: "Подпись к фото",
})
// Аудио, видео, файл
info, err := b.UploadMedia("file", "document.pdf", fileData)
if err != nil {
return err
}
b.Send(chat, &maxbot.Document{UploadedInfo: *info})
// UploadFile — устаревший враппер, оставлен для совместимости
token, err := b.UploadFile("image", "photo.jpg", fileData)Стикер, контакт, геолокация и превью ссылки отправляются без загрузки — это не файлы:
b.Send(chat, &maxbot.Sticker{Code: "smile"})
b.Send(chat, &maxbot.Contact{Name: "Alice", VCFPhone: "+71234567890"})
b.Send(chat, &maxbot.Location{Latitude: 55.75, Longitude: 37.61})
b.Send(chat, &maxbot.Share{URL: "https://example.com"})Стикер, контакт и аудио должны быть единственным вложением в сообщении (так требует API) — библиотека
проверяет это на клиенте и вернёт понятную ошибку до отправки запроса, если рядом окажется клавиатура
или другое вложение. Файл (Document) — исключение: его можно комбинировать с одной inline_keyboard.
Bot.Edit и Context.Edit возвращают только error (не *Message).
b.Handle("/edit", func(c maxbot.Context) error {
return c.Edit("Отредактированный текст")
})
// Прямое редактирование через бот
if err := b.Edit(msg, "Новый текст"); err != nil {
log.Println(err)
}import "github.com/demen1n/maxbot/middleware"
// Логирование
b.Handle("/start", handler, middleware.Logger())
// Whitelist пользователей
b.Handle("/admin", adminHandler,
middleware.Whitelist(123456789, 987654321))
// Rate limiting
b.Handle("/weather", weatherHandler,
middleware.RateLimit(5, time.Minute))
// Только приватные чаты
b.Handle("/settings", settingsHandler,
middleware.OnlyPrivate())
// Цепочка middleware
b.Handle("/cmd", handler,
middleware.Chain(
middleware.Logger(),
middleware.AutoRespond(),
middleware.Throttle(5 * time.Second),
))Logger()- логирование запросовAutoRespond()- автоответ на callback queriesRecover()- восстановление после паник, с ответом пользователюWhitelist(ids...)- разрешить только указанным пользователямBlacklist(ids...)- заблокировать указанных пользователейThrottle(duration)- ограничение частоты использованияRateLimit(max, window)- лимит запросов в окне времениOnlyPrivate()- только приватные чатыOnlyGroups()- только групповые чатыIgnoreBots()- игнорировать сообщения от ботовCommandArgs(min, usage)- проверка минимального числа аргументовChain(...)- объединение нескольких middleware
Logger, Whitelist, Blacklist, Throttle, IgnoreBots, RateLimit и Metrics.Middleware()
безопасно вешать и на хендлеры обновлений без отправителя (OnMessageRemoved,
OnMessageChatCreated, OnBotRemoved и т.п.) — при отсутствии c.Sender() они не паникуют, а
пропускают апдейт дальше (Whitelist — отказывает, как неизвестному пользователю).
Паника внутри хендлера не убивает бота: Bot.ProcessUpdate сама перехватывает её и превращает в
обычную ошибку, которая идёт по тому же пути, что и return err — в Logger и в OnError из
Settings. Без OnError эта ошибка попадёт только в Logger, а если он тоже не задан — в никуда,
поэтому OnError стоит задавать всегда.
middleware.Recover() дополняет эту защиту на уровне конкретного хендлера: перехватывает панику
раньше (до общего пути ошибок), может вызвать свой колбэк и сразу отвечает пользователю
"❌ Произошла внутренняя ошибка" вместо тишины:
b.Handle("/start", handler, middleware.Recover())// Нажатие кнопки "Начать"
b.Handle(maxbot.OnBotStarted, func(c maxbot.Context) error {
return c.Send("Добро пожаловать! deeplink: " + c.Update().Payload)
})
// Бот добавлен в чат / удалён из чата
b.Handle(maxbot.OnBotAdded, func(c maxbot.Context) error {
return c.Send("Привет, " + c.Chat().Title + "!")
})
b.Handle(maxbot.OnBotRemoved, func(c maxbot.Context) error { return nil })
// Вступление и выход участников
b.Handle(maxbot.OnUserAdded, func(c maxbot.Context) error {
return c.Send("Добро пожаловать, " + c.Sender().Name + "!")
})
b.Handle(maxbot.OnUserRemoved, func(c maxbot.Context) error { return nil })
// Изменение названия чата
b.Handle(maxbot.OnChatTitleChanged, func(c maxbot.Context) error {
return c.Send("Чат переименован: " + c.Update().Title)
})
// Редактирование сообщения
b.Handle(maxbot.OnMessageEdited, func(c maxbot.Context) error {
return nil
})
// Удаление сообщения
b.Handle(maxbot.OnMessageRemoved, func(c maxbot.Context) error {
return nil
})
// Чат создан по кнопке Chat (menu.Chat(...))
b.Handle(maxbot.OnMessageChatCreated, func(c maxbot.Context) error {
return c.Send("Чат создан: " + c.Chat().Title)
})webhook := &maxbot.Webhook{
Listen: ":8443",
Endpoint: "/webhook",
URL: "https://example.com/webhook",
Secret: "secret_key", // проверяется через X-Max-Bot-Api-Secret
}
b, err := maxbot.NewBot(maxbot.Settings{
Token: token,
Poller: webhook,
})
// Регистрация webhook в MAX API
b.SetWebhook(webhook.URL, []string{"message_created", "message_callback"}, webhook.Secret)
// Удаление webhook (URL обязателен)
b.DeleteWebhook("https://example.com/webhook")// Получить информацию о чате
chat, err := b.GetChat(chatID)
chat, err := b.GetChatByLink("mygroup")
// Список чатов с пагинацией.
// Deprecated: с июня 2026 GET /chats больше не поддерживается MAX API.
// Замены на стороне API нет — собирайте chat_id сами из входящих апдейтов
// (bot_added, bot_started, message_created и т.д.) и храните их в своей БД.
chats, nextMarker, err := b.GetChats(50, nil)
// Участники с пагинацией
members, nextMarker, err := b.GetChatMembers(chatID, 100, nil)
// Получить администраторов
admins, marker, err := b.GetChatAdmins(chatID)
// Управление участниками
b.KickChatMember(chatID, userID, false) // block=false — просто удалить
b.KickChatMember(chatID, userID, true) // block=true — забанить
b.InviteChatMembers(chatID, []int64{user1, user2})
b.PromoteChatMember(chatID, userID) // все права по умолчанию
b.PromoteChatMember(chatID, userID, maxbot.PermWrite, maxbot.PermPinMessage)
b.DemoteChatMember(chatID, userID)
// Закрепление сообщений
b.PinMessage(chatID, messageID, nil) // notify=nil — серверное значение по умолчанию
notify := true
b.PinMessage(chatID, messageID, ¬ify)
b.UnpinMessage(chatID)
// Действия в чате (typing, отправка фото и т.д.)
b.SendChatAction(chatID, maxbot.ActionTyping)Для работы с комментариями бот должен быть администратором канала с нужными правами
(read_all_messages для чтения, write для публикации, delete для удаления).
// Получить комментарии к посту
comments, err := b.GetComments(postMid, nil, 0, 0, 50)
// Получить конкретный комментарий
comment, err := b.GetComment(postMid, commentID)
// Опубликовать комментарий
comment, err := b.PostComment(postMid, "Текст комментария", "")
// Отредактировать / удалить
b.EditComment(postMid, commentID, "Новый текст")
b.DeleteComment(postMid, commentID)func handler(c maxbot.Context) error {
// Информация об обновлении
c.Bot() // *Bot
c.Update() // Update
c.Message() // *Message
c.Callback() // *CallbackQuery
c.Sender() // *User
c.Chat() // *Chat
// Текст и аргументы
c.Text() // текст сообщения
c.Args() // аргументы команды как слайс
c.Payload() // всё после команды как строка
// Отправка
c.Send("текст", opts...)
c.Reply("текст", opts...)
c.Edit("новый текст", opts...)
c.Delete()
c.Respond() // ответ на callback
// Хранилище
c.Set("key", value)
c.Get("key")
return nil
}metrics := &middleware.Metrics{}
b.Handle("/start", handler, metrics.Middleware())
// Получить статистику
b.Handle("/stats", func(c maxbot.Context) error {
return c.Send(metrics.GetStats())
})