Request Flow
This document details how Telegram updates flow through Alita Robot, from reception to response.
Update Processing Pipeline
Section titled “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
Section titled “Initialization Sequence”When the bot starts (main.go), it performs these steps in order:
-
Health Check Mode Optional
If the
--healthflag is passed, the process performs an HTTP GET to/healthand exits with the status code. This is used by Docker health checks.if os.Args[1] == "--health" {// HTTP GET to /health, exit with status codeos.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)}}() -
Locale Manager Initialization i18n
The singleton
LocaleManagerloads 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 and a 30-second timeout.
b, err := gotgbot.NewBot(config.AppConfig.BotToken, &gotgbot.BotOpts{BotClient: &gotgbot.BaseBotClient{Client: http.Client{Transport: transport, Timeout: 30 * time.Second},},}) -
Connection Pre-warming Performance
Three
GetMecalls run in a background goroutine to establish the HTTP connection pool before real traffic arrives.go func() {for i := 0; i < 3; i++ {b.GetMe(nil) // Establish connection pooltime.Sleep(100 * time.Millisecond)}}() -
Initial Checks Validation
Validates configuration, initializes cache, and performs startup health checks.
alita.InitialChecks(b) // Validates config, initializes cache -
Async Processor Optional
If
EnableAsyncProcessingis configured, the async processor is initialized for non-critical background operations.if config.AppConfig.EnableAsyncProcessing {async.InitializeAsyncProcessor()} -
Dispatcher Creation Core
The dispatcher routes updates to handlers with bounded concurrency. It uses a
TracingProcessorwrapper for trace context propagation in polling mode.dispatcher := ext.NewDispatcher(&ext.DispatcherOpts{Error: errorHandler,MaxRoutines: config.AppConfig.DispatcherMaxRoutines,}) -
Monitoring Systems Observability
Three monitoring subsystems start: background stats collection (every 30s), auto-remediation (GC triggers on memory thresholds), and activity monitoring (per-chat and per-user tracking).
statsCollector = monitoring.NewBackgroundStatsCollector()autoRemediation = monitoring.NewAutoRemediationManager(statsCollector)activityMonitor = monitoring.NewActivityMonitor() -
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)httpServer.RegisterHealth()httpServer.RegisterMetrics()if config.AppConfig.UseWebhooks {httpServer.RegisterWebhook(b, dispatcher, secret, domain)} else {updater.StartPolling(b, pollingOpts)}
Module Loading Order
Section titled “Module Loading Order”After dispatcher creation, modules are loaded via alita/main.go:
func LoadModules(dispatcher *ext.Dispatcher) { // Initialize help system first modules.HelpModule.AbleMap.Init()
// Load help LAST (deferred) to collect all commands defer modules.LoadHelp(dispatcher)
// Core modules (order matters for handler priority) modules.LoadBotUpdates(dispatcher) // Bot status tracking modules.LoadAntispam(dispatcher) // Spam protection modules.LoadLanguage(dispatcher) // Language settings modules.LoadAdmin(dispatcher) // Admin commands modules.LoadPin(dispatcher) // Pin management modules.LoadMisc(dispatcher) // Misc utilities modules.LoadBans(dispatcher) // Ban/kick commands modules.LoadMutes(dispatcher) // Mute commands modules.LoadPurges(dispatcher) // Message purging modules.LoadUsers(dispatcher) // User tracking modules.LoadReports(dispatcher) // Report system modules.LoadDev(dispatcher) // Developer tools modules.LoadLocks(dispatcher) // Chat locks modules.LoadFilters(dispatcher) // Message filters modules.LoadAntiflood(dispatcher) // Flood protection modules.LoadNotes(dispatcher) // Notes system modules.LoadConnections(dispatcher) // Chat connections modules.LoadDisabling(dispatcher) // Command disabling modules.LoadRules(dispatcher) // Rules management modules.LoadWarns(dispatcher) // Warning system modules.LoadGreetings(dispatcher) // Welcome messages modules.LoadCaptcha(dispatcher) // CAPTCHA verification modules.LoadBlacklists(dispatcher) // Blacklist system modules.LoadMkdCmd(dispatcher) // Markdown commands}Handler Registration Pattern
Section titled “Handler Registration Pattern”Each module registers handlers using gotgbot’s handler system:
func LoadBans(dispatcher *ext.Dispatcher) { // Register module in help system HelpModule.AbleMap.Store(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
Section titled “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
Section titled “Handler Groups”Handlers can be assigned to groups for priority control:
// Negative group = higher priority (runs first)dispatcher.AddHandlerToGroup(handler, -10)
// Group 0 = defaultdispatcher.AddHandler(handler) // Same as group 0
// Positive group = lower prioritydispatcher.AddHandlerToGroup(handler, 10)Permission Check Flow
Section titled “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, false) { return ext.EndGroups }
// 2. Require user to be admin if !chat_status.RequireUserAdmin(b, ctx, nil, user.Id, false) { return ext.EndGroups }
// 3. Require bot to be admin if !chat_status.RequireBotAdmin(b, ctx, nil, false) { return ext.EndGroups }
// 4. Check specific permission (restrict members) if !chat_status.CanUserRestrict(b, ctx, nil, user.Id, false) { return ext.EndGroups }
// 5. Check bot has same permission if !chat_status.CanBotRestrict(b, ctx, nil, false) { return ext.EndGroups }
// Proceed with ban logic...}Permission Functions Reference
Section titled “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
Section titled “Response Patterns”Handler Return Values
Section titled “Handler Return Values”// Stop processing, no more handlers runreturn ext.EndGroups
// Continue to next handler in same groupreturn ext.ContinueGroups
// Error propagates to dispatcher error handlerreturn errResponse Actions
Section titled “Response Actions”// Reply to the triggering messagemsg.Reply(b, "Response text", &gotgbot.SendMessageOpts{ ParseMode: helpers.HTML,})
// Send new message to chatb.SendMessage(chat.Id, "Message text", nil)
// Edit existing messagemsg.EditText(b, "New text", nil)
// Delete messagemsg.Delete(b, nil)
// Answer callback queryquery.Answer(b, &gotgbot.AnswerCallbackQueryOpts{ Text: "Notification text",})Async Processing
Section titled “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 protectiongo 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
Section titled “Error Handling in Request Flow”Dispatcher Error Handler
Section titled “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
Section titled “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 errorsNext Steps
Section titled “Next Steps”- Module Pattern - Creating new feature modules
- Caching - Redis cache integration
- Project Structure - File organization