galigo

package module
v0.0.0-...-2b16ddc Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 10, 2026 License: MIT Imports: 8 Imported by: 0

README

galigo

Go Reference CI Go Report Card codecov

A production-grade Go library for the Telegram Bot API with built-in resilience.

galigo is designed for high-load environments where reliability matters. Unlike libraries that treat errors as exceptional, galigo treats rate limits, network failures, and API instability as expected conditions — handling them automatically so you can focus on your bot's logic.

Features

  • Resilient — Circuit breaker prevents cascading failures; only trips on 5xx/network errors, not user errors
  • Respectful — Smart rate limiting with per-chat and global limits; auto-handles 429 Retry-After
  • Secure — Tokens auto-redacted from logs and error messages; TLS 1.2+ enforced
  • Complete — Full Telegram Bot API coverage including Stars, Gifts, Business, and Forum Topics
  • Flexible — Use the unified Bot type or import only sender/ or receiver/ packages
  • Modern — Built for Go 1.25+ with generics, iterators, and structured logging

Installation

go get github.com/prilive-com/galigo

Requirements: Go 1.25 or later

Quick Start

package main

import (
    "context"
    "log"
    "os"
    "os/signal"
    "syscall"

    "github.com/prilive-com/galigo"
)

func main() {
    bot, err := galigo.New(os.Getenv("TELEGRAM_BOT_TOKEN"),
        galigo.WithPolling(30, 100),
        galigo.WithRetries(3),
        galigo.WithRateLimit(30.0, 5),
    )
    if err != nil {
        log.Fatal(err)
    }
    defer bot.Close()

    ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
    defer cancel()

    if err := bot.Start(ctx); err != nil {
        log.Fatal(err)
    }

    for update := range bot.Updates() {
        if update.Message != nil {
            bot.SendMessage(ctx, update.Message.Chat.ID,
                "Echo: "+update.Message.Text)
        }
    }
}

Documentation

Resource Description
API Reference Full type and method documentation
Configuration Guide Circuit breaker, rate limiting, retry, and options
Error Handling Sentinel errors and recommended actions
Architecture Goroutines, channels, and operational model
Examples Working code for common patterns
Testing Guide Integration testing with galigo-testbot
Telegram Bot API Official Telegram documentation
Examples

Compatibility

Component Version Notes
Go 1.25+ Uses generics, iter.Seq, log/slog
Telegram Bot API 8.0+ Stars, Gifts, Business, Checklists
Platforms Linux, macOS, Windows Pure Go, no CGO
Supported API Methods

galigo implements 150+ methods covering:

  • Messages, media, files, and albums
  • Inline keyboards and callback queries
  • Chat administration and moderation
  • Forum topics and permissions
  • Stickers and custom emoji
  • Payments, Stars, and Gifts
  • Polls, quizzes, and giveaways
  • Webhooks and long polling

For the complete method list, see the API Reference.

Thread Safety

Component Safe Notes
galigo.Bot All methods safe for concurrent use
sender.Client Designed for high-concurrency
receiver.PollingClient Single goroutine fetches, multiple can consume
tg.Update Immutable after creation
Close() Idempotency

Both Bot.Close() and sender.Client.Close() are idempotent — safe to call multiple times or concurrently without panicking. This is important for:

  • defer bot.Close() combined with explicit shutdown paths
  • Graceful shutdown handlers that may race with other cleanup code
  • Error recovery scenarios
bot, _ := galigo.New(token)
defer bot.Close()  // Safe even if Close() called elsewhere

// ... later in shutdown handler ...
bot.Close()  // No panic, just a no-op

Webhook mode note: In webhook mode, Bot.Close() does not close the updates channel because HTTP handlers may still be sending updates. Stop your HTTP server first, then call Close().

Error Handling

galigo provides typed errors for precise handling:

import (
    "errors"
    "github.com/prilive-com/galigo/tg"
)

_, err := bot.SendMessage(ctx, chatID, text)
if err != nil {
    switch {
    case errors.Is(err, tg.ErrBotBlocked):
        // User blocked the bot — remove from database
    case errors.Is(err, tg.ErrTooManyRequests):
        // Rate limited — already retried, consider backing off
    case errors.Is(err, tg.ErrMessageNotFound):
        // Message was deleted — update local state
    case errors.Is(err, tg.ErrCircuitOpen):
        // Circuit breaker open — Telegram API may be down
    default:
        var apiErr *tg.APIError
        if errors.As(err, &apiErr) {
            log.Printf("API error %d: %s", apiErr.Code, apiErr.Description)
        }
    }
}
Error Reference
Error When
ErrBotBlocked User blocked the bot
ErrBotKicked Bot removed from group
ErrChatNotFound Chat doesn't exist
ErrMessageNotFound Message was deleted
ErrMessageNotModified Edit had no changes
ErrTooManyRequests Rate limited (429)
ErrUnauthorized Invalid bot token
ErrForbidden Missing permissions
ErrCircuitOpen Circuit breaker tripped
ErrMaxRetries All retry attempts failed

Configuration

Bot Options
bot, err := galigo.New(token,
    // Receiving mode (choose one)
    galigo.WithPolling(30, 100),          // Long polling
    galigo.WithWebhook(8443, "secret"),   // Webhook

    // Resilience
    galigo.WithRetries(3),                // Max retry attempts
    galigo.WithRateLimit(30.0, 5),        // Global: 30 req/s, burst 5

    // Behavior
    galigo.WithLogger(slog.Default()),    // Custom logger
    galigo.WithAllowedUpdates("message", "callback_query"),
)
Environment Variables
Variable Default Description
TELEGRAM_BOT_TOKEN Bot token from @BotFather

For advanced configuration, see the sender and receiver package documentation.

Modular Usage

Use only the packages you need:

// Only sending messages
import "github.com/prilive-com/galigo/sender"

client, _ := sender.New(token, sender.WithRetries(3))
client.SendMessage(ctx, sender.SendMessageRequest{
    ChatID: chatID,
    Text:   "Hello!",
})

// Only receiving updates
import "github.com/prilive-com/galigo/receiver"

updates := make(chan tg.Update, 100)
poller := receiver.NewPollingClient(token, updates, logger, cfg)
poller.Start(ctx)

Resilience

Circuit Breaker

Prevents cascading failures when Telegram is unavailable:

  • Opens after 50% failure rate (minimum 3 requests in 60s window)
  • Half-open state after 30s timeout
  • Only server errors (5xx) and network errors trip the breaker
  • Client errors (4xx) never trip — prevents self-inflicted outages
Rate Limiting

Respects Telegram's limits automatically:

  • Global: 30 requests/second (configurable)
  • Per-chat: 1 request/second per chat (configurable)
  • 429 handling: Reads retry_after from response, waits automatically
Retry Strategy

Transient failures are retried with exponential backoff:

  • Base wait: 500ms → 1s → 2s → 4s (capped at 30s)
  • Cryptographic jitter prevents thundering herd
  • Only retries network errors and 5xx responses

Testing

# Unit tests
go test ./...

# With race detector
go test -race ./...

# With coverage
go test -coverprofile=coverage.out ./...
Integration Tests

galigo includes a testbot for validating against the real Telegram API:

export TESTBOT_TOKEN="your-token"
export TESTBOT_CHAT_ID="your-chat-id"
export TESTBOT_ADMINS="your-user-id"

go run ./cmd/galigo-testbot --run all

See docs/testing.md for complete testing documentation.

Dependencies

Package Purpose
sony/gobreaker/v2 Circuit breaker
golang.org/x/time Rate limiting

Testing only: stretchr/testify

Contributing

Contributions are welcome! Please:

  1. Read the Contributing Guide
  2. Ensure tests pass: go test ./...
  3. Run linter: golangci-lint run
  4. Follow existing code style

Security

Found a vulnerability? Please report it privately:

Do not open public issues for security vulnerabilities.

License

MIT License — use freely in personal and commercial projects.

Documentation

Overview

Package galigo provides a unified Go library for Telegram Bot API.

galigo combines receiving updates (webhook/long polling) and sending messages into a single, modern library with built-in resilience patterns.

Quick Start

bot, err := galigo.New(token,
    galigo.WithPolling(30, 100),
    galigo.WithRetries(5),
)
if err != nil {
    log.Fatal(err)
}
defer bot.Close()

for update := range bot.Updates() {
    bot.SendMessage(ctx, update.Message.Chat.ID, "Echo: "+update.Message.Text)
}

Separate Receiver/Sender

For microservices that only need one capability:

// Only receive updates
import "github.com/prilive-com/galigo/receiver"
recv, _ := receiver.New(token)

// Only send messages
import "github.com/prilive-com/galigo/sender"
send, _ := sender.New(token)

Shared Types

All Telegram types are in the tg subpackage:

import "github.com/prilive-com/galigo/tg"
var msg tg.Message
var user tg.User

Features

  • Dual mode: webhook or long polling
  • Circuit breaker with sony/gobreaker
  • Per-chat and global rate limiting
  • Retry with exponential backoff and crypto jitter
  • TLS 1.2+ enforcement
  • Token auto-redaction in logs and errors
  • Structured logging with slog
  • OpenTelemetry-ready
  • Go 1.22+ features: integer range loops, improved generics

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Bot

type Bot struct {
	// contains filtered or unexported fields
}

Bot is the unified Telegram bot client combining receiver and sender.

func New

func New(token string, opts ...Option) (*Bot, error)

New creates a new unified Bot.

func (*Bot) Acknowledge

func (b *Bot) Acknowledge(ctx context.Context, cb *tg.CallbackQuery) error

Acknowledge silently acknowledges a callback query.

func (*Bot) Answer

func (b *Bot) Answer(ctx context.Context, cb *tg.CallbackQuery, opts ...sender.AnswerOption) error

Answer answers a callback query.

func (*Bot) Close

func (b *Bot) Close() error

Close releases all resources. Close is idempotent; subsequent calls are no-ops.

In polling mode, Close() waits for the poll loop to exit (via Stop()), then closes the updates channel. It is safe to range over Updates() until it closes.

In webhook mode, the updates channel is NOT closed because the HTTP server may still be accepting connections. Stop your HTTP server first, then call Close(). If you need to drain the channel after stopping the server, use a timeout select loop.

func (*Bot) Copy

func (b *Bot) Copy(ctx context.Context, e tg.Editable, toChatID tg.ChatID, opts ...sender.CopyOption) (*tg.MessageID, error)

Copy copies a message.

func (*Bot) Delete

func (b *Bot) Delete(ctx context.Context, e tg.Editable) error

Delete deletes a message.

func (*Bot) Edit

func (b *Bot) Edit(ctx context.Context, e tg.Editable, text string, opts ...sender.EditOption) (*tg.Message, error)

Edit edits a message text.

func (*Bot) Forward

func (b *Bot) Forward(ctx context.Context, e tg.Editable, toChatID tg.ChatID, opts ...sender.ForwardOption) (*tg.Message, error)

Forward forwards a message.

func (*Bot) IsHealthy

func (b *Bot) IsHealthy() bool

IsHealthy returns health status for K8s probes.

func (*Bot) SendMessage

func (b *Bot) SendMessage(ctx context.Context, chatID tg.ChatID, text string, opts ...SendOption) (*tg.Message, error)

SendMessage sends a text message.

func (*Bot) SendPhoto

func (b *Bot) SendPhoto(ctx context.Context, chatID tg.ChatID, photo string, opts ...PhotoOption) (*tg.Message, error)

SendPhoto sends a photo from URL or file_id. For uploading files, use SendPhotoFile.

func (*Bot) SendPhotoFile

func (b *Bot) SendPhotoFile(ctx context.Context, chatID tg.ChatID, photo sender.InputFile, opts ...PhotoOption) (*tg.Message, error)

SendPhotoFile sends a photo from InputFile (supports upload).

func (*Bot) Sender

func (b *Bot) Sender() *sender.Client

Sender returns the underlying sender client for advanced usage.

func (*Bot) Start

func (b *Bot) Start(ctx context.Context) error

Start begins receiving updates.

func (*Bot) Stop

func (b *Bot) Stop()

Stop gracefully stops the bot.

func (*Bot) Updates

func (b *Bot) Updates() <-chan tg.Update

Updates returns the updates channel.

func (*Bot) WebhookHandler

func (b *Bot) WebhookHandler() *receiver.WebhookHandler

WebhookHandler returns the HTTP handler for webhook mode.

type Option

type Option func(*botConfig)

Option configures the Bot.

func WithAllowedUpdates

func WithAllowedUpdates(types ...string) Option

WithAllowedUpdates filters update types.

func WithDeleteWebhook

func WithDeleteWebhook(delete bool) Option

WithDeleteWebhook deletes existing webhook before polling.

func WithLogger

func WithLogger(logger *slog.Logger) Option

WithLogger sets a custom logger.

func WithPolling

func WithPolling(timeout, limit int) Option

WithPolling configures long polling mode.

func WithPollingMaxErrors

func WithPollingMaxErrors(max int) Option

WithPollingMaxErrors sets max consecutive errors.

func WithRateLimit

func WithRateLimit(globalRPS float64, burst int) Option

WithRateLimit sets rate limiting.

func WithRetries

func WithRetries(max int) Option

WithRetries sets max retry attempts.

func WithUpdateBufferSize

func WithUpdateBufferSize(size int) Option

WithUpdateBufferSize sets the updates channel buffer size.

func WithWebhook

func WithWebhook(port int, secret string) Option

WithWebhook configures webhook mode.

type PhotoOption

type PhotoOption func(*sender.SendPhotoRequest)

PhotoOption configures send photo requests.

func PhotoSilent

func PhotoSilent() PhotoOption

PhotoSilent disables notification for photo.

func WithPhotoCaption

func WithPhotoCaption(caption string) PhotoOption

WithPhotoCaption sets the photo caption.

func WithPhotoKeyboard

func WithPhotoKeyboard(kb *tg.InlineKeyboardMarkup) PhotoOption

WithPhotoKeyboard sets the reply keyboard.

func WithPhotoParseMode

func WithPhotoParseMode(mode tg.ParseMode) PhotoOption

WithPhotoParseMode sets parse mode for caption.

type SendOption

type SendOption func(*sender.SendMessageRequest)

SendOption configures send message requests.

func Silent

func Silent() SendOption

Silent disables notification.

func WithKeyboard

func WithKeyboard(kb *tg.InlineKeyboardMarkup) SendOption

WithKeyboard sets the reply keyboard.

func WithParseMode

func WithParseMode(mode tg.ParseMode) SendOption

WithParseMode sets the parse mode.

func WithReplyTo

func WithReplyTo(messageID int) SendOption

WithReplyTo sets the reply-to message ID.

Directories

Path Synopsis
cmd
galigo-testbot command
examples
echo command
Example: Simple echo bot using galigo
Example: Simple echo bot using galigo
internal
scrub
Package scrub provides security helpers for removing sensitive data from errors.
Package scrub provides security helpers for removing sensitive data from errors.
testutil
Package testutil provides testing utilities for galigo.
Package testutil provides testing utilities for galigo.
validate
Package validate provides validation utilities for Telegram API requests.
Package validate provides validation utilities for Telegram API requests.
Package receiver provides Telegram update receiving via webhook or long polling.
Package receiver provides Telegram update receiving via webhook or long polling.
Package sender provides Telegram message sending with resilience features.
Package sender provides Telegram message sending with resilience features.
Package tg provides core Telegram types shared by receiver and sender.
Package tg provides core Telegram types shared by receiver and sender.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL