Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 33 additions & 27 deletions bot.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,36 +30,42 @@ type Middleware func(next HandlerFunc) HandlerFunc
type HandlerFunc func(ctx context.Context, bot *Bot, update *models.Update)
type MatchFunc func(update *models.Update) bool

// Bot represents Telegram Bot main object
// Bot represents the main Telegram Bot instance with configuration and state management.
// It handles updates processing, middleware execution, and provides methods for Telegram API calls.
// The bot supports both polling and webhook modes with configurable concurrency and error handling.
type Bot struct {
lastUpdateID int64

url string
token string
pollTimeout time.Duration
skipGetMe bool
webhookSecretToken string
testEnvironment bool
workers int
notAsyncHandlers bool

defaultHandlerFunc HandlerFunc

errorsHandler ErrorsHandler
debugHandler DebugHandler

middlewares []Middleware

handlersMx sync.RWMutex
handlers []handler

client HttpClient
isDebug bool
checkInitTimeout time.Duration

allowedUpdates AllowedUpdates

updates chan *models.Update
// Connection and authentication
url string // Telegram API server URL
token string // Bot token from BotFather
pollTimeout time.Duration // Timeout for long polling requests
skipGetMe bool // Skip getMe call during initialization
webhookSecretToken string // Secret token for webhook validation
testEnvironment bool // Use Telegram test environment

// Concurrency and execution control
workers int // Number of worker goroutines for processing updates
notAsyncHandlers bool // Execute handlers synchronously instead of in goroutines

// Handler management
defaultHandlerFunc HandlerFunc // Fallback handler when no specific handler matches
middlewares []Middleware // Middleware chain applied to all handlers
handlersMx sync.RWMutex // Mutex for thread-safe handler operations
handlers []handler // Registered update handlers

// Error and debug handling
errorsHandler ErrorsHandler // Custom error handler
debugHandler DebugHandler // Custom debug handler

// HTTP client and debugging
client HttpClient // HTTP client for API requests
isDebug bool // Enable debug logging
checkInitTimeout time.Duration // Timeout for bot initialization checks

// Update filtering and processing
allowedUpdates AllowedUpdates // Filter which update types to receive
updates chan *models.Update // Channel for incoming updates processing
}

// New creates new Bot instance
Expand Down
16 changes: 7 additions & 9 deletions common.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ import (
"time"
)

// escape special symbols in text for MarkdownV2 parse mode
const shouldBeEscaped = "_*[]()~`>#+-=|{}.!"

var shouldBeEscaped = "_*[]()~`>#+-=|{}.!"

// EscapeMarkdown escapes special symbols for Telegram MarkdownV2 syntax
// EscapeMarkdown escapes all special symbols for Telegram MarkdownV2 syntax.
// Use this function to safely display user input or dynamic content in MarkdownV2 messages.
// Example: EscapeMarkdown("Hello_world") returns "Hello\_world"
func EscapeMarkdown(s string) string {
var result []rune
for _, r := range s {
Expand All @@ -29,7 +29,9 @@ func EscapeMarkdown(s string) string {
return string(result)
}

// EscapeMarkdownUnescaped escapes unescaped special symbols for Telegram Markdown v2 syntax
// EscapeMarkdownUnescaped escapes only unescaped special symbols for Telegram MarkdownV2 syntax.
// This function preserves existing escaping and only adds backslashes where needed.
// Use when working with partially formatted MarkdownV2 text that may already contain escaped characters.
func EscapeMarkdownUnescaped(s string) string {
var result []rune
var escaped bool
Expand All @@ -48,10 +50,6 @@ func EscapeMarkdownUnescaped(s string) string {
return string(result)
}

// log functions

// random string generator

const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"

const (
Expand Down
2 changes: 1 addition & 1 deletion get_updates.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ type getUpdatesParams struct {

type AllowedUpdates []string

// GetUpdates https://core.telegram.org/bots/api#getupdates
// See Telegram API docs: https://core.telegram.org/bots/api#getupdates
func (b *Bot) getUpdates(ctx context.Context, wg *sync.WaitGroup) {
defer wg.Done()

Expand Down
57 changes: 40 additions & 17 deletions handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,37 +7,49 @@ import (
"github.com/go-telegram/bot/models"
)

// HandlerType defines the type of Telegram update content that handler should process.
// Each type corresponds to different data sources within update structure.
type HandlerType int

const (
HandlerTypeMessageText HandlerType = iota
HandlerTypeCallbackQueryData
HandlerTypeCallbackQueryGameShortName
HandlerTypePhotoCaption
HandlerTypeMessageText HandlerType = iota // Handles text content of regular messages
HandlerTypeCallbackQueryData // Handles callback data from inline keyboard buttons
HandlerTypeCallbackQueryGameShortName // Handles game short names from game callback queries
HandlerTypePhotoCaption // Handles caption text of photo messages
)

// MatchType defines how the handler pattern should be matched against incoming content.
// Different match types provide various levels of flexibility for content filtering.
type MatchType int

const (
MatchTypeExact MatchType = iota
MatchTypePrefix
MatchTypeContains
MatchTypeExact MatchType = iota // Exact string match (content == pattern)
MatchTypePrefix // Prefix match (content starts with pattern)
MatchTypeContains // Contains match (pattern found anywhere in content)
// Bot command match (finds /pattern as bot command).
// When registering, command is specified without /. Example: "start", "help", etc.
MatchTypeCommand
// Bot command at message start only (strict command matching).
// When registering, command is specified without /. Example: "start", "help", etc.
MatchTypeCommandStartOnly

matchTypeRegexp
matchTypeFunc
// Internal match types (not for public use)
matchTypeRegexp // Regular expression matching (use RegisterHandlerRegexp)
matchTypeFunc // Custom function matching (use RegisterHandlerMatchFunc)
)

// handler represents internal handler configuration with matching logic and execution function.
// Contains all necessary data for determining if update should be processed by this handler.
type handler struct {
id string
handlerType HandlerType
matchType MatchType
handler HandlerFunc

pattern string
re *regexp.Regexp
matchFunc MatchFunc
id string // Unique handler identifier for registration/unregistration
handlerType HandlerType // Type of content to extract from update
matchType MatchType // How to match extracted content against pattern
handler HandlerFunc // Function to execute when match is found

// Pattern matching fields (used based on matchType)
pattern string // String pattern for basic matching
re *regexp.Regexp // Compiled regex for regexp matching
matchFunc MatchFunc // Custom function for function-based matching
}

func (h handler) match(update *models.Update) bool {
Expand Down Expand Up @@ -106,6 +118,9 @@ func (h handler) match(update *models.Update) bool {
return false
}

// RegisterHandlerMatchFunc registers a handler with custom matching function.
// This provides maximum flexibility for complex matching logic that cannot be expressed with standard match types.
// Returns handler ID that can be used to unregister the handler later.
func (b *Bot) RegisterHandlerMatchFunc(matchFunc MatchFunc, f HandlerFunc, m ...Middleware) string {
b.handlersMx.Lock()
defer b.handlersMx.Unlock()
Expand All @@ -124,6 +139,9 @@ func (b *Bot) RegisterHandlerMatchFunc(matchFunc MatchFunc, f HandlerFunc, m ...
return id
}

// RegisterHandlerRegexp registers a handler with regular expression pattern matching.
// The regex will be applied to content extracted based on handlerType.
// Returns handler ID that can be used to unregister the handler later.
func (b *Bot) RegisterHandlerRegexp(handlerType HandlerType, re *regexp.Regexp, f HandlerFunc, m ...Middleware) string {
b.handlersMx.Lock()
defer b.handlersMx.Unlock()
Expand All @@ -143,6 +161,9 @@ func (b *Bot) RegisterHandlerRegexp(handlerType HandlerType, re *regexp.Regexp,
return id
}

// RegisterHandler registers a handler with specified pattern and match type.
// This is the most commonly used method for registering handlers with standard matching logic.
// Returns handler ID that can be used to unregister the handler later.
func (b *Bot) RegisterHandler(handlerType HandlerType, pattern string, matchType MatchType, f HandlerFunc, m ...Middleware) string {
b.handlersMx.Lock()
defer b.handlersMx.Unlock()
Expand All @@ -162,6 +183,8 @@ func (b *Bot) RegisterHandler(handlerType HandlerType, pattern string, matchType
return id
}

// UnregisterHandler removes a previously registered handler by its ID.
// The ID is returned when registering handlers with any of the Register* methods.
func (b *Bot) UnregisterHandler(id string) {
b.handlersMx.Lock()
defer b.handlersMx.Unlock()
Expand Down
Loading