Skip to content

Releases: mr-linch/go-tg

v0.18.0: Bot API 9.4 & Webhook Reply Control

09 Feb 16:53
9690b87

Choose a tag to compare

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 qualities
  • UserProfileAudios — user profile audios
  • ChatOwnerLeft / ChatOwnerChanged — service messages for ownership changes
  • ButtonStyle enum — danger (red), success (green), primary (blue)
  • UniqueGiftModelRarity enum — uncommon, rare, epic, legendary

New methods:

  • GetUserProfileAudios — get user profile audios
  • SetMyProfilePhoto / RemoveMyProfilePhoto — manage bot profile photo

New fields:

  • InlineKeyboardButton / KeyboardButton: IconCustomEmojiID, Style with chaining .WithIconCustomEmojiID(), .WithStyle()
  • Video.Qualities — list of available video qualities
  • ChatFullInfo.FirstProfileAudio — first profile audio
  • User.AllowsUsersToCreateTopics — bot allows topic creation in private chats
  • UniqueGift.IsBurned, UniqueGiftModel.Rarity
  • Message.ChatOwnerLeft, Message.ChatOwnerChanged + corresponding MessageType values
  • CreateForumTopic now 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

07 Feb 22:20

Choose a tag to compare

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 needed

Slice 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 type
  • Update.Msg() / Update.MsgID() / Update.ChatID() — message accessors
  • Message.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-written ReactionTypeEmoji* vars)
  • DiceEmoji — dice animation types
  • PollType, 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() // InputMedia

Variadic 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 + enum

ReactionTypeEmoji.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
  • MessageType enum 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

06 Feb 21:50
40ada5b

Choose a tag to compare

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 over int64 with Time(), 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:

  1. Telegram doesn't document old fields - Chat.Bio and similar moved to ChatFullInfo. The API still returns them, but they're not in the docs, so the generator doesn't see them.

  2. 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: int64UnixTime, *DateTime()field.Time()
  • GetChat()ChatFullInfo
  • Signatures: EditMessageMedia, EditMessageLiveLocation, SendPoll, SendInvoice, CreateInvoiceLink
  • Thumbnail(FileArg)Thumbnail(InputFile)
  • *Ids*IDs
  • NewInlineKeyboardButtonCallbackNewInlineKeyboardButtonCallbackData
  • ...and possibly more

Infrastructure

  • Makefile → Taskfile
  • Git hooks via lefthook
  • golangci-lint v2.8

v0.15.0: Bot API v7.2

29 Apr 13:58
39cc533

Choose a tag to compare

v0.14.0: CallbackDataFilter & TextMessageCallBuilder

19 Mar 18:43
ab4b6bb

Choose a tag to compare

What's Changed

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

12 Mar 14:39
5cf133d

Choose a tag to compare

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 be nil if the request is made with DoVoid method;
  • invoker - function for calling the next interceptor or the actual request.

Contrib package has some useful interceptors:

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

18 Feb 13:02
c6ec119

Choose a tag to compare

⚠️ This release is not compatible with previous releases. Bot API v7.0 has many changes in methods and types. Since types and methods are generated from official documentation, backward compatibility is broken.

Major breaking changes:

  • Types:
    • Fields Message.Forward* replaced with Message.ForwardOrigin.*
    • Change type of Message.PinnedMessage from Message to MaybeInaccessibleMessage
    • Renaming field of Message and type UserShared -> UsersShared
    • Change type of CallbackQuery.Message from Message to MaybeInaccessibleMessage
    • InputTextMessageContent.DisableWebPagePreview replaced with InputTextMessageContent.LinkPreviewOptions
  • Methods:
    • Replace *.DisableWebPagePreview(...) with *.LinkPreviewOptions(...)
    • Replace *.AllowSendingWithoutReply(...) and *.ReplyToMessageID(...) with *.ReplyParameters(...)
image image

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

23 Sep 17:01
114f0b5

Choose a tag to compare

What's Changed

  • chore(deps): bump actions/checkout from 3 to 4 by @dependabot in #130
  • upgrade: Bot API 6.9 & non-empty Story type fix by @mr-linch in #132

Full Changelog: v0.10.0...v0.11.0

Bot API

image

v0.10.0

18 Aug 17:35
01dca14

Choose a tag to compare

What's Changed

Full Changelog: v0.9.1...v0.10.0

v0.9.1

24 May 23:33
e1a72e6

Choose a tag to compare

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