Releases: mr-linch/go-tg
v0.18.0: Bot API 9.4 & Webhook Reply Control
Telegram Bot API v9.4
Updated from v9.3 to v9.4 (February 9, 2026). Same-day support was possible thanks to the automated update pipeline from v0.16.0 — only minor generator config tweaks were needed (#167).
New types:
VideoQuality— available video qualitiesUserProfileAudios— user profile audiosChatOwnerLeft/ChatOwnerChanged— service messages for ownership changesButtonStyleenum —danger(red),success(green),primary(blue)UniqueGiftModelRarityenum —uncommon,rare,epic,legendary
New methods:
GetUserProfileAudios— get user profile audiosSetMyProfilePhoto/RemoveMyProfilePhoto— manage bot profile photo
New fields:
InlineKeyboardButton/KeyboardButton:IconCustomEmojiID,Stylewith chaining.WithIconCustomEmojiID(),.WithStyle()Video.Qualities— list of available video qualitiesChatFullInfo.FirstProfileAudio— first profile audioUser.AllowsUsersToCreateTopics— bot allows topic creation in private chatsUniqueGift.IsBurned,UniqueGiftModel.RarityMessage.ChatOwnerLeft,Message.ChatOwnerChanged+ correspondingMessageTypevaluesCreateForumTopicnow supports private chats with users
Webhook Reply Control
New WithWebhookReply(bool) option controls whether the first Reply() call returns its response in the webhook HTTP body (bypassing Client and interceptors). Enabled by default. Disable with WithWebhookReply(false) to route all replies through the interceptor chain.
BaseUpdate.Reply Shortcut
Reply(ctx, v) is now available directly on all typed update wrappers (MessageUpdate, CallbackQueryUpdate, etc.) via BaseUpdate embedding:
// Before — through .Update
msg.Update.Reply(ctx, msg.React(reaction))
// Now — direct shortcut
msg.Reply(ctx, msg.React(reaction))Both are fully compatible — msg.Reply delegates to msg.Update.Reply.
Update.User() Filtering
Update.User() now accepts optional UpdateType args to restrict which update types return a user:
// Only return user for messages and callback queries
update.User(tg.UpdateTypeMessage, tg.UpdateTypeCallbackQuery)Bug Fix: User() and SenderChat() for CallbackQuery
User() previously used Msg() which returns CallbackQuery.Message (the bot's message with buttons), causing it to return the message author instead of the button clicker. Now correctly returns CallbackQuery.From. Same fix applied to SenderChat().
Migration Note
Union parameters: variadic → slice
Slice union parameters (SendMediaGroup, SendPaidMedia, AnswerInlineQuery, SetMessageReaction, SetPassportDataErrors) now accept concrete []X slices instead of variadic ...XClass. The same applies to tgb wrappers (React, AnswerMediaGroup, AnswerPaidMedia, Answer).
Wrap variadic args with XOf() helpers or use slice literals:
// v0.17.0
client.SendMediaGroup(chatID, photo1, photo2)
msg.React(tg.NewReactionTypeEmoji(tg.ReactionEmojiThumbsUp))
// v0.18.0
client.SendMediaGroup(chatID, tg.InputMediaOf(photo1, photo2))
msg.React(tg.ReactionTypeOf(tg.NewReactionTypeEmoji(tg.ReactionEmojiThumbsUp)))v0.17.0: Polymorphic Unions, Keyboard Builders & Update Helpers
What's New
Polymorphic Union Types
Union type constructors now return concrete variant types with *Class interfaces for compile-time type safety:
// v0.16.0
media := tg.NewInputMediaPhoto(file) // returned InputMedia struct
// v0.17.0
photo := tg.NewInputMediaPhoto(file) // returns *InputMediaPhoto
photo.WithCaption("hello") // fluent With* builders on variant
photo.AsInputMedia() // wrap into InputMedia when neededSlice union parameters are now variadic:
// v0.16.0
client.SendMediaGroup(chatID, []tg.InputMedia{photo1, photo2}).Do(ctx)
// v0.17.0
client.SendMediaGroup(chatID, photo1, photo2).Do(ctx)New interfaces: InputMediaClass, InputPaidMediaClass, InlineQueryResultClass, ReactionTypeClass, BotCommandScopeClass, MenuButtonClass, and more.
Keyboard Builders
New fluent API for building inline and reply keyboards (in addition to existing constructors):
kb := tg.NewInlineKeyboard().
Callback("Yes", "yes").Callback("No", "no").
Row().
URL("Docs", "https://example.com").
Adjust(2)kb := tg.NewReplyKeyboard().
Text("Option 1").Text("Option 2").
Row().
RequestContact("Share Contact").
Resize().OneTime()Update Helper Methods
56 auto-generated typed convenience methods on tgb update wrappers:
router.Message(func(ctx context.Context, msg *tgb.MessageUpdate) error {
return msg.AnswerPhoto(photoFile).Caption("Here!").DoVoid(ctx)
})
router.CallbackQuery(func(ctx context.Context, cbq *tgb.CallbackQueryUpdate) error {
return cbq.EditText("Updated!").DoVoid(ctx)
})Methods include: AnswerPhoto, AnswerVideo, AnswerDocument, EditText, EditCaption, EditMedia, Delete, Pin, Forward, Copy, React, Ban, Unban, Restrict, and more.
Data Extraction Helpers
New convenience methods for common data extraction patterns:
Update.Chat()/Update.User()/Update.SenderChat()— extract from any update typeUpdate.Msg()/Update.MsgID()/Update.ChatID()— message accessorsMessage.TextOrCaption()/Message.FileID()/Message.IsInaccessible()User.FullName()/Chat.FullName()
ParseMode Enhancements
tg.HTML.Mention("John", userID)
tg.HTML.CustomEmoji("⭐", emojiID)
tg.HTML.PreLanguage("go", code)
tg.MD2.ExpandableBlockquote("Long text...")
tg.MD2.Escapef("Price: %s USD", price)MediaMessageCallBuilder
Unified builder for sending media with shared caption/markup, materializable as different send/edit calls:
media := msg.AnswerMedia().Caption("Photo!").ParseMode(tg.HTML)
media.AsSendPhoto(photoFile).DoVoid(ctx)
media.AsSendVideo(videoFile).DoVoid(ctx)Expanded Enums
ReactionEmoji— 70+ emoji reaction constants (replaces hand-writtenReactionTypeEmoji*vars)DiceEmoji— dice animation typesPollType,MaskPositionPoint,StickerFormat,EncryptedPassportElementType,Currency- All enum constants now have string value comments:
ChatTypePrivate // "private"
Breaking Changes
Union constructors return variant types
All union constructors (NewInputMediaPhoto, NewReactionTypeEmoji, NewBotCommandScopeDefault, etc.) now return *Variant instead of the union struct. Use .AsUnionType() to wrap:
// Before
media := tg.NewInputMediaPhoto(file) // InputMedia
// After
media := tg.NewInputMediaPhoto(file).AsInputMedia() // InputMediaVariadic union parameters
Methods accepting union slices now use variadic ...XClass parameters:
// Before
client.SendMediaGroup(chatID, []tg.InputMedia{m1, m2})
client.AnswerInlineQuery(queryID, []tg.InlineQueryResult{r1, r2})
// After
client.SendMediaGroup(chatID, m1, m2)
client.AnswerInlineQuery(queryID, r1, r2)Reaction constants replaced with enum
ReactionTypeEmoji* variables removed. Use ReactionEmoji enum:
// Before
tg.ReactionTypeEmojiThumbsUp // var ReactionType
// After
tg.NewReactionTypeEmoji(tg.ReactionEmojiThumbsUp) // constructor + enumReactionTypeEmoji.Emoji field type changed from string to ReactionEmoji.
Keyboard builder methods renamed
Two methods on ReplyKeyboardMarkup were renamed:
WithResizeKeyboardMarkup()→WithResizeKeyboard()WithOneTimeKeyboardMarkup()→WithOneTimeKeyboard()
All constructors and other With* methods remain unchanged.
ParseMode interface extended
5 new methods added to the ParseMode interface: Escapef, Mention, CustomEmoji, PreLanguage, ExpandableBlockquote. Custom ParseMode implementations must add these methods.
Other Improvements
- Go doc links in all generated comments (
[TypeName],[Client.Method]) - 231
With*builder methods for optional fields on union variants MessageTypeenum fixed to exclude metadata fields- New examples:
media-gallery,book-bot,parse-mode
Full Changelog: v0.16.0...v0.17.0
v0.16.0: New Code Generator & Bot API 9.3
Hey! Big update after a long break. The library has been running in production all this time and survived several complex bots - so the foundation is battle-tested.
What's New
Code Generator
Completely rewrote the generator, now it's part of the repository (gen/). Generates 80% of all code - parses Telegram's HTML documentation and outputs types, methods, and tgb infrastructure. Configured via YAML with expr expression support.
Automatic Updates
GitHub Actions checks for Telegram Bot API updates daily and creates PRs automatically.
Forward Compatibility
Union types and enums now handle unknown values properly - UnknownVariant and IsUnknown().
Telegram Bot API v9.3
Full and better support for all features.
Improvements
- Enum types -
ChatType,StickerType,MessageEntityType,ChatAction,UpdateType,MessageType,ChatMemberStatus, etc. Were strings, now typed constants. - Variant constructors - 19 button constructors generated automatically (
NewInlineKeyboardButtonURL,NewKeyboardButtonRequestChat, etc.) UnixTime- wrapper overint64withTime(),IsZero()methods.
Breaking Changes
Sorry for the inconvenience, but there was no other way. Some changes aren't even due to Telegram - the old generator was just broken:
-
Telegram doesn't document old fields -
Chat.Bioand similar moved toChatFullInfo. The API still returns them, but they're not in the docs, so the generator doesn't see them. -
Types were parsed incorrectly - union types were interfaces, now they're structs. Enums are also generated properly now.
What will break:
- Go 1.18+ → Go 1.21+
- Union types: interface → struct
- Dates:
int64→UnixTime,*DateTime()→field.Time() GetChat()→ChatFullInfo- Signatures:
EditMessageMedia,EditMessageLiveLocation,SendPoll,SendInvoice,CreateInvoiceLink Thumbnail(FileArg)→Thumbnail(InputFile)*Ids→*IDsNewInlineKeyboardButtonCallback→NewInlineKeyboardButtonCallbackData- ...and possibly more
Infrastructure
- Makefile → Taskfile
- Git hooks via lefthook
- golangci-lint v2.8
v0.15.0: Bot API v7.2
Full Changelog: v0.14.0...v0.15.0
v0.14.0: CallbackDataFilter & TextMessageCallBuilder
What's Changed
- tg: add reactions enum by @mr-linch in #144
- tgb: Text Message Call Builder by @mr-linch in #145
- tgb: CallbackDataFilter and CallbackDataCodec by @mr-linch in #140
Examples: https://github.com/mr-linch/go-tg/blob/main/examples/menu/main.go
Full Changelog: v0.13.0...v0.14.0
v0.13.0: Interceptors
Interceptors are used to modify or process the request before it is sent to the server and the response before it is returned to the caller. It's like a [tgb.Middleware], but for outgoing requests.
All interceptors should be registered on the client before the request is made.
client := tg.New("<TOKEN>",
tg.WithClientInterceptors(
tg.Interceptor(func(ctx context.Context, req *tg.Request, dst any, invoker tg.InterceptorInvoker) error {
started := time.Now()
// before request
err := invoker(ctx, req, dst)
// after request
log.Print("call %s took %s", req.Method, time.Since(started))
return err
}),
),
)Arguments of the interceptor are:
ctx- context of the request;req- request object tg.Request;dst- pointer to destination for the response, can benilif the request is made withDoVoidmethod;invoker- function for calling the next interceptor or the actual request.
Contrib package has some useful interceptors:
- InterceptorRetryFloodError - retry request if the server returns a flood error. Parameters can be customized via options;
- InterceptorRetryInternalServerError - retry request if the server returns an error. Parameters can be customized via options;
- InterceptorMethodFilter - call underlying interceptor only for specified methods;
- InterceptorDefaultParseMethod - set default
parse_modefor messages if not specified.
Interceptors are called in the order they are registered.
Example of using retry flood interceptor: examples/retry-flood
Full Changelog: v0.12.0...v0.13.0
Breaking Bot API v7.0 and v7.1 support and other minor changes
Major breaking changes:
- Types:
- Fields
Message.Forward*replaced withMessage.ForwardOrigin.* - Change type of
Message.PinnedMessagefromMessagetoMaybeInaccessibleMessage - Renaming field of
Messageand typeUserShared->UsersShared - Change type of
CallbackQuery.MessagefromMessagetoMaybeInaccessibleMessage InputTextMessageContent.DisableWebPagePreviewreplaced withInputTextMessageContent.LinkPreviewOptions
- Fields
- Methods:
- Replace
*.DisableWebPagePreview(...)with*.LinkPreviewOptions(...) - Replace
*.AllowSendingWithoutReply(...)and*.ReplyToMessageID(...)with*.ReplyParameters(...)
- Replace
Other changes
- allow to use custom func for get ip from request by @mr-linch in #133
- chore(deps): bump actions/setup-go from 4 to 5 by @dependabot in #134
- chore(deps): bump codecov/codecov-action from 3 to 4 by @dependabot in #136
- Add blockquote support for HTML and MD2 by @adasauce in #138
- chore(deps): bump golangci/golangci-lint-action from 3 to 4 by @dependabot in #139
New Contributors
Full Changelog: v0.11.0...v0.12.0
Bot API v6.9 support and non-empty `Story` type fix
What's Changed
- chore(deps): bump actions/checkout from 3 to 4 by @dependabot in #130
- upgrade: Bot API 6.9 & non-empty
Storytype fix by @mr-linch in #132
Full Changelog: v0.10.0...v0.11.0
Bot API

v0.10.0
What's Changed
- chore(deps): bump github.com/stretchr/testify from 1.8.3 to 1.8.4 by @dependabot in #127
- feat: upgrade to v6.8 by @mr-linch in #129
Full Changelog: v0.9.1...v0.10.0
v0.9.1
What's Changed
- chore(deps): bump github.com/stretchr/testify from 1.8.2 to 1.8.3 by @dependabot in #122
- fix: unmarshal error for GetChatMenuButton by @mr-linch in #125
Full Changelog: v0.9.0...v0.9.1