Filters
Narrow what a handler matches by passing one or more Filter values to On. The pattern string handles the body match; filters handle everything else.
The idea
gogram's On accepts an arbitrary number of Filter values after the pattern and handler. A handler fires only when every filter returns true. The type is a tiny interface: Check(*NewMessage) bool + CheckCallback(*CallbackQuery) bool. Built-in filters and your own implementations are interchangeable.
Built-in filters
The library ships a set of Filter* values that match the common predicates. The big ones:
- FilterPrivate / FilterGroup / FilterChannel
- Match by chat kind. Each also has a
CheckCallbackimplementation so the same filter works on inline-keyboard buttons. - FilterCommand
- True when the message looks like a bot command (starts with
/or!). - FilterReply / FilterForward / FilterEdited
- True for replies, forwarded messages, and messages that have been edited at least once.
- FilterIncoming / FilterOutgoing
- Direction of the message relative to the current session.
- FilterFromBot
- Sender is a bot. Useful when a userbot wants to react only to messages from other bots.
- FilterMention
- The current user is mentioned in the message.
- HasMedia / HasPhoto / HasVideo / HasDocument / HasAudio / HasSticker / HasAnimation / HasVoice / HasVideoNote / HasContact / HasLocation / HasVenue / HasPoll
- Match by media kind.
- IsText
- The message has non-empty text. Pairs well with a
HasPhotofilter when you want photos that came with a caption.
Passing filters to On
client.On("message:*", onlyPrivate, telegram.FilterPrivate)
client.On("message:*", onlyPhotos, telegram.FilterPhoto)
client.On("message:*", onlyEdited, telegram.FilterEdited)client.On("message:*", onlyPrivate, telegram.FilterPrivate)
client.On("message:*", onlyPhotos, telegram.FilterPhoto)
client.On("message:*", onlyEdited, telegram.FilterEdited)Stack multiple filters to require all of them:
client.On("message:*", onMatch,
telegram.FilterGroup,
telegram.FilterPhoto,
)client.On("message:*", onMatch,
telegram.FilterGroup,
telegram.FilterPhoto,
)The handler runs only when the message is from a group and contains a photo.
Pattern strings
The string in "message:body" handles the body match. Three shapes:
*matches anything.- A literal string matches by substring.
- A regex pattern prefixed with
r:— e.g. the example below pulls an order number out of a command.
client.On("message:r:^/order\\s+(\\d+)$", onOrder)client.On("message:r:^/order\\s+(\\d+)$", onOrder)Custom filters
Anything that satisfies the Filter interface works. The library provides telegram.Func as a small helper that wraps a function into a filter:
mine := telegram.Func(func(m *telegram.NewMessage) bool {
return m.SenderID() == myAdminID
})
client.On("message:*", onAdminMessage, mine)mine := telegram.Func(func(m *telegram.NewMessage) bool {
return m.SenderID() == myAdminID
})
client.On("message:*", onAdminMessage, mine)Use this for the long tail — checking caller permissions, looking up flags in your database, gating on feature flags — without writing a struct.
Global middleware
For cross-cutting concerns that should run on every update regardless of the handler — timing, recovery, structured logging — use client.Use instead of filters:
client.Use(func(handler telegram.Handler) telegram.Handler {
return func(ctx telegram.HandlerCtx) error {
start := time.Now()
err := handler(ctx)
log.Printf("[%v] %T finished in %s", err == nil, ctx.Update, time.Since(start))
return err
}
})client.Use(func(handler telegram.Handler) telegram.Handler {
return func(ctx telegram.HandlerCtx) error {
start := time.Now()
err := handler(ctx)
log.Printf("[%v] %T finished in %s", err == nil, ctx.Update, time.Since(start))
return err
}
})Middleware wraps the dispatcher pipeline; filters narrow individual handlers. Use the right tool: middleware for cross-cutting, filters for routing.
