Skip to content
Alita Robot
Esc
navigateopen⌘Jpreview
On this page

Request Flow

How Telegram updates are processed through the Alita Robot pipeline.

This document details how Telegram updates flow through Alita Robot, from reception to response.

Update Processing Pipeline

+-------------+     +----------------+     +------------+     +----------+
|  Telegram   | --> |  HTTP/Polling  | --> | Dispatcher | --> | Handlers |
|   Bot API   |     |    Receiver    |     |  (Router)  |     | (Modules)|
+-------------+     +----------------+     +------------+     +----------+
                                                |                   |
                                                |                   v
                                                |            +------+------+
                                                |            | Permission  |
                                                |            |   Checks    |
                                                |            +------+------+
                                                |                   |
                                                |                   v
                                                |            +------+------+
                                                |            |  Database   |
                                                |            |  / Cache    |
                                                |            +------+------+
                                                |                   |
                                                v                   v
                                          +----------+       +----------+
                                          |  Error   |       | Response |
                                          | Handler  |       |  to User |
                                          +----------+       +----------+

Initialization Sequence

When the bot starts (main.go), it performs these steps in order:

Health Check Mode

Optional

If the --health flag is passed, the process performs an HTTP GET to /health and exits with the status code. This is used by Docker health checks.

 if len(os.Args) > 1 && (os.Args[1] == "--health" || os.Args[1] == "-health") {
    // HTTP GET to /health, exit with status code
    os.Exit(0)
}

Version Check

Optional

If the --version, -version, or -v flag is passed, the process prints the bot version and exits immediately without initializing any services.

 if len(os.Args) > 1 && (os.Args[1] == "--version" || os.Args[1] == "-version" || os.Args[1] == "-v") {
    fmt.Println(config.AppConfig.BotVersion)
    os.Exit(0)
}

Panic Recovery Setup

Safety

A top-level deferred recovery ensures the process logs the panic and exits cleanly rather than crashing silently.

defer func() {
    if r := recover(); r != nil {
        log.Errorf("[Main] Panic recovered: %v", r)
        os.Exit(1)
    }
}()

Cache Initialization

Infrastructure

The Redis cache is initialized with connection retry logic (exponential backoff). Startup fails if Redis is unavailable.

if err := cache.InitCache(); err != nil {
    log.Fatalf("Failed to initialize cache: %v", err)
}

Locale Manager Initialization

i18n

The singleton LocaleManager loads embedded YAML translation files for all supported languages.

localeManager := i18n.GetManager()
localeManager.Initialize(&Locales, "locales", i18n.DefaultManagerConfig())

OpenTelemetry Tracing Initialization

Observability

Sets up distributed tracing with OTLP or console exporters based on environment configuration (OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_SERVICE_NAME, OTEL_TRACES_SAMPLE_RATE).

tracing.InitTracing()

HTTP Transport Configuration

Networking

Connection pooling is configured for outbound HTTP requests to the Telegram API.

httpTransport := &http.Transport{
    MaxIdleConns:        config.AppConfig.HTTPMaxIdleConns,
    MaxIdleConnsPerHost: config.AppConfig.HTTPMaxIdleConnsPerHost,
    IdleConnTimeout:     120 * time.Second,
    ForceAttemptHTTP2:   true,
}

Bot Client Creation

Core

The gotgbot client is initialized with the configured HTTP transport, a 30-second timeout, and the optional custom Bot API URL.

b, err := gotgbot.NewBot(config.AppConfig.BotToken, &gotgbot.BotOpts{
    BotClient: &gotgbot.BaseBotClient{
        Client: http.Client{Transport: transport, Timeout: 30 * time.Second},
        DefaultRequestOpts: &gotgbot.RequestOpts{
            APIURL: resolveBotAPIURL(config.AppConfig.ApiServer),
        },
    },
})

Initial Checks

Validation

Ensures the bot user exists in the database before modules load, providing the foreign-key anchor used by downstream records. Configuration has already been loaded and validated by the config package.

if err := alita.InitialChecks(b); err != nil {
    log.Fatalf("Initial checks failed: %v", err)
}

Dispatcher Creation

Core

The dispatcher routes updates to handlers with bounded concurrency. It uses a TracingProcessor wrapper for trace context propagation in polling mode.

dispatcher := ext.NewDispatcher(&ext.DispatcherOpts{
    Error:       errorHandler,
    MaxRoutines: config.AppConfig.DispatcherMaxRoutines,
    Processor:   tracing.TracingProcessor{},
})

Monitoring Systems

Observability

Monitoring systems are started based on configuration.

if config.AppConfig.EnableBackgroundStats {
    statsCollector = monitoring.NewBackgroundStatsCollector()
}
if config.AppConfig.EnablePerformanceMonitoring {
    autoRemediation = monitoring.NewAutoRemediationManager(statsCollector)
}
activityMonitor = monitoring.NewActivityMonitor() // Always starts

HTTP Server & Mode Selection

Core

The unified HTTP server registers health, metrics, and optionally pprof endpoints. Then either webhook or polling mode is activated.

httpServer := httpserver.New(config.AppConfig.HTTPPort, appStartTime)
httpServer.RegisterHealth()
httpServer.RegisterMetrics()
httpServer.RegisterDBMetrics()
if config.AppConfig.EnablePPROF {
    httpServer.RegisterPPROF()
}

if config.AppConfig.UseWebhooks {
    httpServer.RegisterWebhook(b, dispatcher, secret, domain)
} else {
    updater.StartPolling(b, pollingOpts)
}

Module Loading Order

After dispatcher creation and before Telegram updates are accepted, postInit loads modules and starts the persisted captcha lifecycle:

func LoadModules(dispatcher *ext.Dispatcher) {
    // Initialize help system first
    modules.DefaultHelpRegistry().AbleMap = make(map[string]bool)

    // Load help LAST (deferred) to collect all commands
    defer modules.LoadHelp(dispatcher)

    // Loads all registered modules in priority order
    modules.LoadAllModules(dispatcher)
}

alita.LoadModules(dispatcher)
if err := modules.StartCaptchaLifecycle(bot); err != nil {
    log.Fatalf("[Captcha] Failed to start lifecycle: %v", err)
}

Captcha startup recovery releases incomplete attempts, finalizes expired attempts, and reschedules still-valid attempts. It then starts the periodic attempt-cleanup and scheduled-unmute workers. A recovery failure aborts startup instead of accepting updates with an unknown captcha state.

Registered modules include: BotUpdates, Antispam, Languages, Admin, Approvals, Pins, Misc, Bans, Mutes, Purges, Users, Reports, Dev, Locks, Filters, Antiflood, Notes, Connections, Disabling, Rules, Warns, Greetings, Captcha, AntiRaid, Blacklists, Reactions, Formatting, Backup.

Handler Registration Pattern

Each module registers handlers using gotgbot’s handler system:

func LoadBans(dispatcher *ext.Dispatcher) {
    // Register module in help system
    DefaultHelpRegistry().AbleMap[bansModule.moduleName] = true

    // Command handlers
    dispatcher.AddHandler(handlers.NewCommand("ban", bansModule.ban))
    dispatcher.AddHandler(handlers.NewCommand("kick", bansModule.kick))
    dispatcher.AddHandler(handlers.NewCommand("unban", bansModule.unban))

    // Callback query handlers
    dispatcher.AddHandler(handlers.NewCallback(
        callbackquery.Prefix("restrict"),
        bansModule.restrictButtonHandler,
    ))
}

Handler Types

Type Registration Trigger
Command handlers.NewCommand("cmd", fn) /cmd messages
Callback handlers.NewCallback(filter, fn) Button presses
Message handlers.NewMessage(filter, fn) Text messages
ChatMember handlers.NewChatMemberUpdated(filter, fn) Member updates

Handler Groups

Handlers can be assigned to groups for priority control:

// Negative group = higher priority (runs first)
dispatcher.AddHandlerToGroup(handler, -10)

// Group 0 = default
dispatcher.AddHandler(handler)  // Same as group 0

// Positive group = lower priority
dispatcher.AddHandlerToGroup(handler, 10)

Permission Check Flow

Most admin commands follow this permission checking pattern:

func (m moduleStruct) ban(b *gotgbot.Bot, ctx *ext.Context) error {
    chat := ctx.EffectiveChat
    user := ctx.EffectiveSender.User
    msg := ctx.EffectiveMessage

    // 1. Require group chat (not private)
    if !chat_status.RequireGroup(b, ctx, nil) {
        return ext.EndGroups
    }

    // 2. Require user to be admin
    if !chat_status.RequireUserAdmin(b, ctx, nil, user.Id) {
        return ext.EndGroups
    }

    // 3. Require bot to be admin
    if !chat_status.RequireBotAdmin(b, ctx, nil) {
        return ext.EndGroups
    }

    // 4. Check specific permission (restrict members)
    if !chat_status.CanUserRestrict(b, ctx, nil, user.Id) {
        return ext.EndGroups
    }

    // 5. Check bot has same permission
    if !chat_status.CanBotRestrict(b, ctx, nil) {
        return ext.EndGroups
    }

    // Proceed with ban logic...
}

Permission Functions Reference

Function Purpose When to Use
RequireGroup Ensures chat is group/supergroup Group-only commands
RequirePrivate Ensures chat is private PM-only commands
RequireUserAdmin User must be admin Admin commands
RequireBotAdmin Bot must be admin Commands needing bot admin
RequireUserOwner User must be creator Owner-only commands
CanUserRestrict User can ban/mute Ban/mute commands
CanBotRestrict Bot can ban/mute Ban/mute commands
CanUserDelete User can delete messages Purge commands
CanBotDelete Bot can delete messages Purge commands
CanUserPin User can pin messages Pin commands
CanBotPin Bot can pin messages Pin commands
CanUserPromote User can promote/demote Admin management
CanBotPromote Bot can promote/demote Admin management
IsUserAdmin Check if user is admin Conditional logic
IsUserInChat Check if user is member User validation
IsUserBanProtected Check if user is protected Before ban/kick

Response Patterns

Handler Return Values

// Stop processing, no more handlers run
return ext.EndGroups

// Continue to subsequent handler groups
return ext.ContinueGroups

// Error propagates to dispatcher error handler
return err

Response Actions

// Reply to the triggering message
msg.Reply(b, "Response text", &gotgbot.SendMessageOpts{
    ParseMode: helpers.HTML,
})

// Send new message to chat
b.SendMessage(chat.Id, "Message text", nil)

// Edit existing message
msg.EditText(b, &gotgbot.EditMessageTextOpts{Text: "New text"})

// Delete message
msg.Delete(b, nil)

// Answer callback query
query.Answer(b, &gotgbot.AnswerCallbackQueryOpts{
    Text: "Notification text",
})

Async Processing

Non-critical operations can be processed asynchronously:

// Fire and forget (with panic recovery)
go func() {
    defer func() {
        if r := recover(); r != nil {
            log.Error("Panic in async operation")
        }
    }()

    // Async work here
}()

// With timeout protection
go func() {
    ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
    defer cancel()

    select {
    case <-time.After(2 * time.Second):
        // Do delayed work
    case <-ctx.Done():
        log.Warn("Operation timed out")
    }
}()

Error Handling in Request Flow

Dispatcher Error Handler

Error: func(b *gotgbot.Bot, ctx *ext.Context, err error) ext.DispatcherAction {
    // 1. Recover from panics
    defer error_handling.RecoverFromPanic("DispatcherErrorHandler", "Main")

    // 2. Extract context for logging
    logFields := log.Fields{
        "update_id":  ctx.UpdateId,
        "error_type": fmt.Sprintf("%T", err),
    }

    // 3. Check for expected/suppressible errors
    if helpers.IsExpectedTelegramError(err) {
        log.WithFields(logFields).Warn("Expected Telegram API error")
        return ext.DispatcherActionNoop
    }

    // 4. Log the error
    log.WithFields(logFields).Error("Handler error")

    // 5. Continue processing other updates
    return ext.DispatcherActionNoop
}

Common Error Patterns

// Log and return error (propagates to dispatcher)
if err != nil {
    log.Error(err)
    return err
}

// Log but continue (non-fatal)
if err != nil {
    log.Warn("Non-fatal error:", err)
}

// Silent failure for expected cases
_, _ = msg.Delete(b, nil)  // Ignore delete errors

Next Steps

Was this page helpful?