How the Zakirullin Files Database Interface Works: In-Memory Session Storage Explained

The Zakirullin Files database interface implements a lightweight, per-user session store built on Go's sync.Map that keeps Telegram bot state entirely in memory, persisting only the last keyboard message ID to a temporary file.

The zakirullin/files.md project avoids external database dependencies by managing transient bot data through a simple yet thread-safe interface defined in server/db/db.go. This design prioritizes speed and simplicity, storing everything from pending command expectations to photo message IDs in memory structures keyed by Telegram user ID.

Core Architecture and Design Philosophy

The database interface centers on the DB struct, a minimal holder for the current user's identifier that provides methods for all session state operations. Unlike traditional databases requiring connection pools or migration scripts, this implementation creates a new DB instance per user session via db.NewDB(userID int64) *DB (server/db/db.go:27-30).

All transient data lives in sync.Map structures keyed by the Telegram user ID combined with type-specific suffixes (e.g., "12345:inputExpectations"). This approach guarantees safe concurrent access across goroutines without explicit locking mechanisms. Only one value—the last keyboard message ID—survives process restarts, serialized to the OS temporary directory at <tmp>/<uid>.msgid.

Database Operations and Method Reference

Keyboard Message Persistence

The interface provides file-backed storage for the last interactive keyboard message, allowing the bot to edit or delete previous UI elements. The LastKeyboardMsgID() (int, bool) method (server/db/db.go:31-44) attempts to read from the temporary file, returning false if missing or malformed. To update this value, SetLastKeyboardMsgID(ID int) writes the ID to disk (server/db/db.go:46-48), while DelLastKeyboardMsgID() removes the file entirely (server/db/db.go:50-52).

Input Expectations and Command Flow

To handle multi-step conversational commands, the database stores pending input expectations using SetInputExpectation(cmd tg.Cmd) (server/db/db.go:64-66). When the next user message arrives, the bot retrieves the pending command via InputExpectation() *tg.Cmd (server/db/db.go:54-62), which returns nil if no expectation exists. After processing, DelInputExpectation() clears the pending state (server/db/db.go:68-70).

Message-to-Resource Mapping

The bot maintains a bidirectional mapping between Telegram message IDs and file identifiers using SetHashOrPathByMsgID(msgID int, value string) (server/db/db.go:82-85). This enables callback handlers to resolve which file or hash a user clicked. The lookup method HashOrPathByMsgID(msgID int) (string, bool) (server/db/db.go:72-80) distinguishes paths (starting with /) from content hashes by examining the stored string prefix.

Command History Tracking

For "repeat last command" functionality, the interface stores the most recent command name and arguments. SetRecentCommand(cmd string) (server/db/db.go:96-98) and SetRecentCommandParams(params []string) (server/db/db.go:109-111) persist these values, retrievable via RecentCommand() (string, bool) (server/db/db.go:87-94) and RecentCommandParams() ([]string, bool) (server/db/db.go:100-107).

Image Deduplication

To prevent re-uploading identical photos, the database tracks sent image message IDs in a per-user slice. AddImgMsgID(msgID int) appends new IDs (server/db/db.go:13-21), while ImgMsgID() ([]int, bool) retrieves the complete list (server/db/db.go:23-31). The DelImgMsgID() method clears this history when needed (server/db/db.go:33-37).

Practical Implementation Example

The following pattern demonstrates typical usage within the bot's request handler, showing initialization, state management, and cleanup:

// Initialize per-user database handle (typically in worker.go or server.go)
db := db.NewDB(update.Message.From.ID)

// Persist keyboard message for later editing
db.SetLastKeyboardMsgID(sentKeyboardMsgID)

// Set expectation for a two-step command (e.g., move file)
db.SetInputExpectation(tg.Cmd{
    Name: "mv",
    Args: []string{"/source/file.md"},
})

// Later, when processing the follow-up message
if exp := db.InputExpectation(); exp != nil {
    exp.Args = append(exp.Args, update.Message.Text)
    executeCommand(exp)
    db.DelInputExpectation()
}

// Map a rendered message to its underlying resource
db.SetHashOrPathByMsgID(renderedMsgID, "QmXyz123...")
if resource, ok := db.HashOrPathByMsgID(clickedMsgID); ok {
    if strings.HasPrefix(resource, "/") {
        // Handle as file path
    } else {
        // Handle as IPFS hash
    }
}

// Track command history for repeat functionality
db.SetRecentCommand("search")
db.SetRecentCommandParams([]string{"keyword", "limit:10"})

// Manage photo deduplication
db.AddImgMsgID(photoMsgID)
if existingIDs, ok := db.ImgMsgID(); ok {
    for _, id := range existingIDs {
        // Check for duplicates before uploading
    }
}

Key Source Files and Integration Points

File Responsibility
server/db/db.go Core implementation of all database methods and sync.Map key generation
server/worker.go Instantiates DB per incoming Telegram update and injects it into bot handlers
cmd/server/server.go Entry point that demonstrates database initialization patterns
server/bot.go Consumes database methods for command routing and keyboard management

Summary

  • The Zakirullin Files database interface provides a zero-dependency, in-memory session store using Go's sync.Map for thread safety.
  • All data is scoped to individual Telegram user IDs, ensuring isolation between concurrent sessions.
  • Transient state (input expectations, command history, image IDs, message mappings) lives only for the process lifetime.
  • Keyboard message IDs persist to temporary files, enabling the bot to clean up or edit previous interface elements after restarts.
  • The interface supports conversational command flows through input expectations and maintains message-to-resource mappings for interactive file browsing.

Frequently Asked Questions

What makes the Zakirullin Files database interface different from traditional databases?

Unlike PostgreSQL or MongoDB, this interface requires no external server, connection strings, or schema migrations. It stores everything in process memory using sync.Map structures, making it ideal for ephemeral bot state that doesn't require durability across deployments.

How does the interface handle concurrent users?

Each user receives an isolated namespace within the shared sync.Map instances through composite keys like "<userID>:inputExpectations". The DB struct itself is instantiated per request with the specific UserID, ensuring that db.NewDB(12345) cannot access data belonging to db.NewDB(67890).

Which data survives a bot restart?

Only the last keyboard message ID persists to disk, written to a temporary file at <os.TempDir()>/<userID>.msgid via SetLastKeyboardMsgID. All other state—including pending commands, message mappings, and command history—is intentionally ephemeral and resets when the process restarts.

How are message IDs mapped to file paths or hashes?

The bot calls SetHashOrPathByMsgID(msgID, value) when rendering a file block, storing either an absolute path (starting with /) or a content hash. Callback handlers later use HashOrPathByMsgID(msgID) to resolve which resource the user interacted with, distinguishing file paths from hashes by checking for the leading slash character.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →