Skip to content
Alita Robot
Esc
↑↓navigate↵open⌘Jpreview
On this page

Architecture

High-level architecture and design principles of Alita Robot.

Alita Robot is a modern Telegram group management bot built with Go and the gotgbot library. This document provides an overview of the architectural decisions, technology stack, and design principles that guide the codebase.

Technology Stack

Component Technology Purpose
Language Go 1.26+ Core application runtime
Telegram Library gotgbot v2 Telegram Bot API wrapper
Build System GoReleaser Multi-platform builds and releases
Component Technology Purpose
Database PostgreSQL Persistent data storage
ORM GORM Object-relational mapping
Migrations Custom Engine Schema versioning with transactional execution

The database uses a surrogate key pattern: auto-increment id as PK with external IDs (user_id, chat_id) as unique constraints.

Component Technology Purpose
Caching Redis Distributed caching layer
Client gocache Go cache library with marshaling
Stampede Protection Shared loads One database load per key among concurrent callers

All cache keys are prefixed with alita: for namespace isolation. TTLs range from 20 seconds (anonymous admin verification) to 1 hour (language preferences).

Component Technology Purpose
Metrics Prometheus Metrics and observability
Tracing OpenTelemetry Distributed tracing with OTLP/console exporters
Health HTTP /health Unified health, metrics, pprof on single port

Architecture Components

Dispatcher

Routes incoming Telegram updates to appropriate handlers. Configurable max goroutines (default: 200) with enhanced error handling and panic recovery.

Module System

Feature modules in alita/modules/ follow a consistent pattern: moduleStruct, handler methods, LoadModule registration. Help module loads last to collect all commands.

Cache Layer

Redis caching with shared-load stampede protection. Per-type TTLs, automatic invalidation on writes, and distributed cache with namespace isolation.

Permission System

Centralized permission validation in alita/utils/chat_status/. Checks are cached to reduce Telegram API calls. Supports anonymous admin detection.

Monitoring

4-tier auto-remediation, activity tracking (per-chat and per-user DAU/WAU/MAU), background stats every 30s, and GC triggers on memory thresholds.

Graceful Shutdown

Central coordinator with LIFO handler execution. Each handler gets panic recovery. Total timeout: 60 seconds.

Core Design Principles

1. Domain Functions Pattern

All database operations are organized by domain in alita/db/{domain}/repository.go files (e.g., alita/db/warns/repository.go, alita/db/filters/repository.go). Each package provides Get/Add/Update/Delete functions for its domain, with surrogate key pattern (auto-increment id as PK, external IDs as unique constraints):

// Example: Direct domain function calls
settings := chats.GetChatSettings(chatId) // package alita/db/chats

2. Command Pipeline

New commands register through helpers.WrapCommand(dispatcher, helpers.CommandDescriptor{...}, handler) in alita/utils/helpers/command_pipeline.go. The wrapper handles cross-cutting concerns uniformly:

  • Permission and precondition checks via CommandDescriptor.RequiredChecks (helpers.RequireGroup(), helpers.RequireUserAdmin(), helpers.CanBotRestrict(), …). The first failing check handles its own feedback (a reply, or silence for a disabled command) and the command returns ext.EndGroups.
  • Panic recovery via error_handling.RecoverFromPanic
  • Ending handler-group iteration after a connected-chat swap (endGroupsAfterChatSwap)
  • Alias and disableable-command registration from the descriptor

alita/utils/helpers/decorators.go only holds the legacy MultiCommand alias loop, which adds no wrapping, and the disableable-command registry (AddCmdToDisableable, IsDisableable, DisableableCommands).

3. Worker Pools

Concurrent processing uses bounded worker pools with panic recovery:

  • Dispatcher: Limited to 200 max goroutines by default (configurable via DISPATCHER_MAX_ROUTINES)
  • Message Pipeline: Concurrent validation stages
  • Bulk Operations: Parallel batch processors with generic framework

4. Redis Caching

Redis-based caching with stampede protection:

  • Distributed cache using Redis for persistence across restarts
  • Concurrent callers for the same key share one database load (cache.GetFromCacheOrLoad)
  • Configurable TTLs per data type (30min - 1hr typically)

Request Flow Diagram

                                    +------------------+
                                    |    Telegram      |
                                    |    Bot API       |
                                    +--------+---------+
                                             |
                          +------------------+------------------+
                          |                                     |
                   Webhook Mode                           Polling Mode
                          |                                     |
                          v                                     v
               +----------+----------+              +-----------+-----------+
               |   HTTP Server       |              |      Updater          |
               |      /webhook       |              |   GetUpdates loop     |
               +----------+----------+              +-----------+-----------+
                          |                                     |
                          +------------------+------------------+
                                             |
                                             v
                                  +----------+----------+
                                  |     Dispatcher      |
                                  | (configurable routines)|
                                  +----------+----------+
                                             |
                         +-------------------+-------------------+
                         |                   |                   |
                         v                   v                   v
                  +------+------+     +------+------+     +------+------+
                  |   Handler   |     |   Handler   |     |   Handler   |
                  |  (Command)  |     | (Callback)  |     |  (Message)  |
                  +------+------+     +------+------+     +------+------+
                         |                   |                   |
                         +-------------------+-------------------+
                                             |
                              +--------------+--------------+
                              |                             |
                              v                             v
                    +---------+----------+        +---------+----------+
                    |   Redis Cache      |        |   PostgreSQL       |
                    |   (cache lookup)    |        |   (via GORM)       |
                    +--------------------+        +--------------------+

Key Subsystems

Dispatcher

The dispatcher routes incoming Telegram updates to appropriate handlers:

  • Configurable max goroutines (default: 200)
  • Enhanced error handler with structured logging
  • Recovery from panics in any handler
dispatcher := ext.NewDispatcher(&ext.DispatcherOpts{
    Error: func(b *gotgbot.Bot, ctx *ext.Context, err error) ext.DispatcherAction {
        // Error handling with structured logging
        return ext.DispatcherActionNoop
    },
    MaxRoutines: config.AppConfig.DispatcherMaxRoutines,
})

Module System

Each feature module follows a consistent pattern:

  1. Define a moduleStruct with module name
  2. Implement handler functions as methods
  3. Register handlers in a LoadModule function
  4. Self-register in init() via RegisterLegacyModule(name, priority, loadFunc)
  5. Loaded collectively via modules.LoadAllModules(dispatcher) in priority order

Cache Layer

Redis caching with stampede protection:

  • Cache Keys: Prefixed with alita: for namespace isolation
  • Stampede Protection: GetFromCacheOrLoad shares one database load per key among concurrent callers
  • TTL Management: Per-type expiration (settings: 30min, language: 1hr)

Monitoring

Comprehensive monitoring subsystems:

  • Activity Monitor: Automatic group activity tracking with configurable thresholds
  • Background Stats: Samples memory and goroutine counts every 30 seconds and database stats every minute, and reports every 5 minutes
  • Auto-Remediation: GC triggers when memory exceeds thresholds

Concurrency Model

Bounded Concurrency

// Dispatcher limits concurrent handler execution
MaxRoutines: 200  // Configurable via DISPATCHER_MAX_ROUTINES

// Hot bulk paths have fixed local limits: purges, flood cleanup, admin checks,
// and multi-member greetings. They are intentionally not configuration knobs.

Shared Cache Loads

alita/db/cache.GetFromCacheOrLoad runs one load per key and shares it among concurrent callers. Each caller keeps its own cancellation and deadline. The shared query has a 30-second deadline (loadWaitTimeout) and is cancelled when its last waiting caller leaves. Failed or cancelled loads are not cached. See Caching.

Graceful Shutdown

// Shutdown manager coordinates cleanup in order
shutdownManager := shutdown.NewManager()
shutdownManager.RegisterHandler(func() error {
    // Cleanup monitoring, database, cache
    return nil
})

Error Handling Strategy

Alita uses a 4-layer error handling hierarchy:

Layer 1: Dispatcher Level

Error: func(b *gotgbot.Bot, ctx *ext.Context, err error) ext.DispatcherAction {
    defer error_handling.RecoverFromPanic("DispatcherErrorHandler", "Main")
    // Log error with structured fields
    return ext.DispatcherActionNoop
}

Layer 2: Worker Level

Worker pools implement panic recovery:

go func() {
    defer func() {
        if r := recover(); r != nil {
            log.WithField("panic", r).Error("Panic in worker")
        }
    }()
    // Worker logic
}()

Layer 3: Handler Level

Handlers outside the pipeline call the chat_status predicates directly and return early:

// Permission checks return early on failure
if !chat_status.RequireUserAdmin(b, ctx, nil, user.Id) {
    return ext.EndGroups
}

Layer 4: Command Pipeline

helpers.WrapCommand adds panic recovery, RequiredChecks permission validation and chat-swap group termination around every command it registers:

// The pipeline wraps the handler with checks, panic recovery and group termination
helpers.WrapCommand(dispatcher, helpers.CommandDescriptor{
    Name:    "cmd",
    Aliases: []string{"alias"},
    RequiredChecks: []helpers.CheckFunc{
        helpers.RequireGroup(),
        helpers.RequireUserAdmin(),
    },
}, handler) // handler: func(c *helpers.CommandContext) error

Database Schema Design

The database uses a surrogate key pattern:

  • Primary Keys: Auto-incremented id field (internal identifier)
  • Business Keys: user_id and chat_id with unique constraints
  • Benefits:
    • Decouples internal schema from Telegram IDs
    • Stable identifiers if external systems change
    • Better performance for joins and indexing

Next Steps

Was this page helpful?