storage

package module
v0.1.1 Latest Latest
Warning

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

Go to latest
Published: May 14, 2026 License: Apache-2.0 Imports: 8 Imported by: 0

README

storage

Persistent storage adapters for [mtgo] Telegram clients — session data, peer cache, and conversation state.

License

Features

  • Six built-in backends: SQLite (pure-Go, no CGO), PostgreSQL, MongoDB, Redis, GORM, SQLC-generated
  • In-memory adapter: storage.NewMemory() for testing and short-lived clients
  • Conformance test suite: verify custom adapters with internal/suite
  • Conversation plugin support: optional ConversationStore interface
  • Portable session strings: export sessions in Telethon/Pyrogram/Kurigram format
  • Update state and dedup: durable update queue with retry tracking

Installation

go get github.com/mtgo-labs/storage

Install only the backend you need:

go get github.com/mtgo-labs/storage/sqlite
# or
go get github.com/mtgo-labs/storage/postgres
# or
go get github.com/mtgo-labs/storage/mongodb

Usage

SQLite
ext, err := sqlite.Open("session.db")
if err != nil {
    log.Fatal(err)
}
defer ext.Close()

client, err := tg.NewClient(mustAtoi(apiID), apiHash, &tg.Config{
    BotToken:    botToken,
    SessionName: "storage_bot",
    SavePeers:   true,
    Storage:     storage.NewAdapter(ext),
})
PostgreSQL
ext, err := postgres.Open(postgres.Config{
    Host:     "localhost",
    Port:     5432,
    User:     "mtgo",
    Password: "secret",
    Database: "mtgo",
    SSLMode:  "require",
})
if err != nil {
    log.Fatal(err)
}
defer ext.Close()

client, err := tg.NewClient(mustAtoi(apiID), apiHash, &tg.Config{
    BotToken:    botToken,
    SessionName: "storage_bot",
    SavePeers:   true,
    Storage:     storage.NewAdapter(ext),
})
MongoDB
ext, err := mongodb.Open(ctx, mongodb.Config{
    URI:      "mongodb://localhost:27017",
    Database: "mtgo",
})
if err != nil {
    log.Fatal(err)
}
defer ext.Close()

client, err := tg.NewClient(mustAtoi(apiID), apiHash, &tg.Config{
    BotToken:    botToken,
    SessionName: "storage_bot",
    SavePeers:   true,
    Storage:     storage.NewAdapter(ext),
})

Note: Backends implement storage.Adapter (SessionStore + PeerStore + Close). The storage.NewAdapter wrapper bridges that interface into the client's Storage interface.

Exporting a Session String
str, err := sqlite.ExportSessionString(sess)

Custom Adapters

Implement the storage.Adapter interface (SessionStore + PeerStore + Close). For conversation plugin support, also implement ConversationStore.

A complete JSON-file example is in examples/custom_storage/.

Verifying with the Test Suite
func TestMyAdapter(t *testing.T) {
    a := myadapter.Open()
    suite.Run(t, a)
}

Individual sub-suites are also available: suite.RunSession, suite.RunPeers, suite.RunConversations.

Architecture

storage.go          — interfaces and domain types (Session, Peer, Conversation, UpdateState)
adapter.go          — wraps Adapter into the client's Storage interface
memory.go           — in-memory adapter (NewMemory) for testing
sqlite/             — SQLite adapter (pure-Go via modernc.org/sqlite)
postgres/           — PostgreSQL adapter (lib/pq)
mongodb/            — MongoDB adapter (mongo-driver/v2)
redis/              — Redis adapter
gorm/               — GORM adapter
sqlc/               — SQLC schema and generated queries for sqlite/postgres
internal/suite/     — conformance test suite for custom adapters
examples/           — custom adapter examples

Contributing

Contributions are welcome! Please open an issue or pull request.

Running Tests
go test ./...

License

Licensed under the Apache License, Version 2.0. See LICENSE for details.

Documentation

Overview

Package storage defines the interfaces and domain types used by mtgo Telegram clients to persist session data, peer cache entries, and conversation state.

The primary interface is Adapter, which combines SessionStore and PeerStore. Implementations that also support the conversations plugin should satisfy ConversationStore as well.

Built-in adapters are provided as sub-modules:

To create a custom adapter, implement the Adapter interface (and optionally ConversationStore). See the ExampleCustomStorage type in this package for a minimal in-memory implementation.

Wiring an adapter into a client

store, _ := sqlite.Open("session.db")
client, _ := telegram.NewClient(apiID, apiHash, telegram.WithStorage(store))

Implementing a custom adapter

A custom adapter must implement SessionStore, PeerStore, and [Close]. To support the conversations plugin, also implement ConversationStore. Use the test suite in github.com/mtgo-labs/storage/internal/suite to verify correctness:

func TestMyAdapter(t *testing.T) {
    a := NewMyAdapter()
    suite.Run(t, a)
}

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ExportSessionString

func ExportSessionString(sess *Session) (string, error)

ExportSessionString encodes session data into a portable string. This is a convenience function for memory-based adapters.

func MustMarshal

func MustMarshal(v interface{}) []byte

MustMarshal marshals v to JSON, panicking on error.

func MustUnmarshal

func MustUnmarshal(data []byte, v interface{})

MustUnmarshal unmarshals data into v, panicking on error. If data is empty, it is a no-op.

func NewAdapter

func NewAdapter(a Adapter) *adapterWrapper

NewAdapter wraps a github.com/mtgo-labs/storage.Adapter so it can be used as Config.Storage in the Telegram client.

Types

type Adapter

type Adapter interface {
	SessionStore
	PeerStore
	Close() error
}

Adapter combines session and peer storage. ConversationStore is optional; adapters that support it should also implement ConversationStore.

type ChannelUpdateState

type ChannelUpdateState struct {
	SessionID string `json:"session_id"`
	ChannelID int64  `json:"channel_id"`
	Pts       int32  `json:"pts"`
}

type Conversation

type Conversation struct {
	ChatID    int64  `json:"chat_id"`
	UserID    int64  `json:"user_id"`
	Name      string `json:"name"`
	Step      int    `json:"step"`
	Data      []byte `json:"data,omitempty"`
	CreatedAt int64  `json:"created_at"`
	UpdatedAt int64  `json:"updated_at"`
}

Conversation holds plugin conversation state.

type ConversationStore

type ConversationStore interface {
	SaveConversation(c *Conversation) error
	LoadConversation(chatID, userID int64) (*Conversation, error)
	DeleteConversation(chatID, userID int64) error
}

ConversationStore persists plugin conversation state. Implementations should create their backing table lazily on first use.

type DCAuthEntry

type DCAuthEntry struct {
	DCID       int    `json:"dc_id"`
	AuthKey    []byte `json:"auth_key"`
	ServerSalt int64  `json:"server_salt"`
	Port       int    `json:"port"`
	ServerAddr string `json:"server_addr"`
}

type DCAuthStore

type DCAuthStore interface {
	SaveDCAuth(entry DCAuthEntry) error
	LoadDCAuth(dcID int) (DCAuthEntry, error)
}

type DurableUpdate

type DurableUpdate struct {
	SessionID string `json:"session_id"`
	ID        string `json:"id"`
	Payload   []byte `json:"payload"`
	Attempts  int    `json:"attempts"`
	LastError string `json:"last_error"`
	CreatedAt int64  `json:"created_at"`
	UpdatedAt int64  `json:"updated_at"`
}

type Peer

type Peer struct {
	ID          int64    `json:"id"`
	Type        PeerType `json:"type"`
	AccessHash  int64    `json:"access_hash"`
	Username    string   `json:"username"`
	Usernames   string   `json:"usernames,omitempty"`
	FirstName   string   `json:"first_name"`
	LastName    string   `json:"last_name"`
	PhoneNumber string   `json:"phone_number,omitempty"`
	IsBot       bool     `json:"is_bot,omitempty"`
	PhotoID     int64    `json:"photo_id,omitempty"`
	Language    string   `json:"language,omitempty"`
	LastUpdated int64    `json:"last_updated"`
}

Peer holds cached peer information.

type PeerCache

type PeerCache interface {
	SavePeers([]Peer) error
	LoadPeers() ([]Peer, error)
	SavePeer(Peer) error
	DeletePeer(id int64) error
}

PeerCache is an optional interface that Storage implementations can satisfy to persist peer entries for the Telegram client's peer cache.

type PeerEntry

type PeerEntry struct {
	ID          int64    `json:"id"`
	Type        PeerType `json:"type"`
	AccessHash  int64    `json:"access_hash"`
	Username    string   `json:"username"`
	Usernames   string   `json:"usernames,omitempty"`
	FirstName   string   `json:"first_name"`
	LastName    string   `json:"last_name"`
	PhoneNumber string   `json:"phone_number,omitempty"`
	IsBot       bool     `json:"is_bot,omitempty"`
	PhotoID     int64    `json:"photo_id,omitempty"`
	Language    string   `json:"language,omitempty"`
	LastUpdated int64    `json:"last_updated,omitempty"`
}

type PeerStore

type PeerStore interface {
	SavePeer(p *Peer) error
	GetPeer(id int64) (*Peer, error)
	GetPeerByUsername(username string) (*Peer, error)
	LoadPeers() ([]*Peer, error)
	DeletePeer(id int64) error
}

PeerStore persists peer cache entries.

type PeerType

type PeerType int

PeerType represents the type of a Telegram peer.

const (
	// PeerTypeUser identifies a Telegram user.
	PeerTypeUser PeerType = 0
	// PeerTypeChat identifies a basic group chat.
	PeerTypeChat PeerType = 1
	// PeerTypeChannel identifies a channel or supergroup.
	PeerTypeChannel PeerType = 2
)

func (PeerType) String

func (p PeerType) String() string

type Session

type Session struct {
	SessionID string `json:"session_id"`
	DC        int    `json:"dc"`
	APIID     int32  `json:"api_id"`
	APIHash   string `json:"api_hash"`
	TestMode  bool   `json:"test_mode"`
	AuthKey   []byte `json:"auth_key"`
	State     []byte `json:"state"`
	UserID    int64  `json:"user_id"`
	IsBot     bool   `json:"is_bot"`
	FirstName string `json:"first_name"`
	LastName  string `json:"last_name"`
	Username  string `json:"username"`
	Date      int    `json:"date"`
	Addr      string `json:"addr"`
	Port      int    `json:"port"`
}

Session holds MTProto session data.

type SessionIDAware

type SessionIDAware interface {
	SetSessionName(name string)
}

SessionIDAware is an optional interface that adapters can implement to receive the session name from the client. The client calls SetSessionName early in connectTransport, before any data access, so the adapter can scope its queries accordingly.

type SessionStore

type SessionStore interface {
	LoadSession() (*Session, error)
	SaveSession(s *Session) error
}

SessionStore persists MTProto session data.

type Storage

type Storage interface {
	SessionID() (string, error)
	SetSessionID(string) error

	DCID() (int, error)
	SetDCID(int) error

	APIID() (int32, error)
	SetAPIID(int32) error

	APIHash() (string, error)
	SetAPIHash(string) error

	TestMode() (bool, error)
	SetTestMode(bool) error

	AuthKey() ([]byte, error)
	SetAuthKey([]byte) error

	UserID() (int64, error)
	SetUserID(int64) error

	IsBot() (bool, error)
	SetIsBot(bool) error

	FirstName() (string, error)
	SetFirstName(string) error

	LastName() (string, error)
	SetLastName(string) error

	Username() (string, error)
	SetUsername(string) error

	Date() (int, error)
	SetDate(int) error

	ServerAddress() (string, error)
	SetServerAddress(string) error

	Port() (int, error)
	SetPort(int) error

	State() ([]byte, error)
	SetState([]byte) error

	// ExportSessionString encodes the session state into a portable string
	// using the Telethon/Pyrogram/Kurigram format:
	//
	//	"1" + base64url( dc_id[1B] + ip[4B|16B] + port[2B big-endian] + auth_key[256B] )
	//
	// WARNING: the returned string contains the full 256-byte authorization key
	// in plaintext. Anyone who obtains this string can fully impersonate the
	// session.
	ExportSessionString() (string, error)

	Close() error
}

Storage is the interface the Telegram client uses to read and write session fields. Implementations must also implement PeerCache for peer persistence and [Close] for cleanup.

Built-in implementations:

  • [MemoryStorage] — in-memory, for testing
  • [SQLiteStorage] — SQLite-backed, created via [NewSQLiteStorage]

Third-party adapters (sqlite, mongodb, postgres) can be bridged via NewAdapter:

store, _ := sqlite.Open("bot.db")
client, _ := tg.NewClient(apiID, apiHash, &tg.Config{
    Storage: storage.NewAdapter(store),
})

func NewMemory

func NewMemory() Storage

type UpdateState

type UpdateState struct {
	SessionID string `json:"session_id"`
	Pts       int32  `json:"pts"`
	Qts       int32  `json:"qts"`
	Date      int32  `json:"date"`
	Seq       int32  `json:"seq"`
}

type UpdateStateStore

type UpdateStateStore interface {
	LoadUpdateState(sessionID string) (*UpdateState, error)
	SaveUpdateState(state *UpdateState) error
	LoadChannelUpdateState(sessionID string, channelID int64) (*ChannelUpdateState, error)
	LoadAllChannelUpdateStates(sessionID string) ([]*ChannelUpdateState, error)
	SaveChannelUpdateState(state *ChannelUpdateState) error
	SaveUpdateDedupKey(sessionID string, key string) (bool, error)
	UpdateDedupKeyExists(sessionID string, key string) (bool, error)
	EnqueueDurableUpdate(update *DurableUpdate) error
	DeleteDurableUpdate(sessionID string, id string) error
	LoadDurableUpdates(sessionID string, limit int) ([]*DurableUpdate, error)
	MarkDurableUpdateFailed(sessionID string, id string, attempts int, lastErr string) error
}

Directories

Path Synopsis
internal
suite
Package suite provides a conformance test suite for storage.Adapter implementations.
Package suite provides a conformance test suite for storage.Adapter implementations.
mongodb module
sqlite module

Jump to

Keyboard shortcuts

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