storage

package
v0.20.0 Latest Latest
Warning

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

Go to latest
Published: Apr 22, 2024 License: MIT Imports: 16 Imported by: 5

Documentation

Overview

Package storage contains common structures for iterating over peer storage.

Index

Examples

Constants

View Source
const LatestVersion = 2

LatestVersion is a latest supported version of data.

Variables

View Source
var ErrPeerNotFound = errors.New("peer not found")

ErrPeerNotFound is a special error to return when peer not found.

View Source
var ErrPeerUnmarshalMustInvalidate = errors.New("outdated data for Peer (cache miss, must invalidate)")

ErrPeerUnmarshalMustInvalidate means that persisted Peer is outdated and must be invalidated.

View Source
var PeerKeyPrefix = []byte("peer") // nolint:gochecknoglobals

PeerKeyPrefix is a key prefix of peer key.

Functions

func ForEach added in v0.5.0

func ForEach(ctx context.Context, iterator PeerIterator, cb func(Peer) error) error

ForEach calls callback on every iterator element.

func UpdateHook added in v0.3.0

func UpdateHook(next telegram.UpdateHandler, storage PeerStorage) telegram.UpdateHandler

UpdateHook creates update hook, to collect peer data from updates.

Example
package main

import (
	"context"
	"fmt"
	"os"
	"os/signal"

	pebbledb "github.com/cockroachdb/pebble"
	"github.com/go-faster/errors"

	"github.com/gotd/td/telegram"
	"github.com/gotd/td/telegram/message"
	"github.com/gotd/td/tg"

	"github.com/gotd/contrib/pebble"
	"github.com/gotd/contrib/storage"
)

func updatesHook(ctx context.Context) error {
	db, err := pebbledb.Open("pebble.db", &pebbledb.Options{})
	if err != nil {
		return errors.Errorf("create pebble storage: %w", err)
	}
	s := pebble.NewPeerStorage(db)

	dispatcher := tg.NewUpdateDispatcher()
	handler := storage.UpdateHook(dispatcher, s)
	client, err := telegram.ClientFromEnvironment(telegram.Options{
		UpdateHandler: handler,
	})
	if err != nil {
		return errors.Errorf("create client: %w", err)
	}
	raw := tg.NewClient(client)
	sender := message.NewSender(raw)

	dispatcher.OnNewMessage(func(ctx context.Context, e tg.Entities, update *tg.UpdateNewMessage) error {
		msg, ok := update.Message.(*tg.Message)
		if !ok {
			return nil
		}

		// Use PeerID to find peer because *Short updates does not contain any entities, so it necessary to
		// store some entities.
		// Storage can be filled using PeerCollector.
		p, err := storage.FindPeer(ctx, s, msg.GetPeerID())
		if err != nil {
			return err
		}

		_, err = sender.To(p.AsInputPeer()).Text(ctx, msg.GetMessage())
		return err
	})

	return client.Run(ctx, func(ctx context.Context) error {
		return telegram.RunUntilCanceled(ctx, client)
	})
}

func main() {
	ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)
	defer cancel()

	if err := updatesHook(ctx); err != nil {
		_, _ = fmt.Fprintf(os.Stderr, "%+v\n", err)
		os.Exit(1)
	}
}
Output:

Types

type Peer

type Peer struct {
	Version   int
	Key       dialogs.DialogKey
	CreatedAt time.Time
	User      *tg.User
	Chat      *tg.Chat
	Channel   *tg.Channel
	Metadata  map[string]any
}

Peer is abstraction for persisted peer object.

Note: unmarshal error ErrPeerUnmarshalMustInvalidate MUST be considered as cache miss and cache entry MUST be invalidated.

The only valid way to marshal and unmarshal Peer is to use UnmarshalJSON, MarshalJSON.

func FindPeer added in v0.3.0

func FindPeer(ctx context.Context, s PeerStorage, p tg.PeerClass) (Peer, error)

FindPeer finds peer using given storage.

func (Peer) AsInputChannel

func (p Peer) AsInputChannel() (*tg.InputChannel, bool)

AsInputChannel tries to convert peer to *tg.InputChannel.

func (Peer) AsInputPeer

func (p Peer) AsInputPeer() tg.InputPeerClass

AsInputPeer tries to convert peer to tg.InputPeerClass.

func (Peer) AsInputUser

func (p Peer) AsInputUser() (*tg.InputUser, bool)

AsInputUser tries to convert peer to *tg.InputUser.

func (*Peer) FromChat added in v0.3.0

func (p *Peer) FromChat(chat tg.ChatClass) bool

FromChat fills Peer object using given tg.ChatClass.

func (*Peer) FromInputPeer

func (p *Peer) FromInputPeer(input tg.InputPeerClass) error

FromInputPeer fills Peer object using given tg.InputPeerClass.

func (*Peer) FromUser added in v0.3.0

func (p *Peer) FromUser(user tg.UserClass) bool

FromUser fills Peer object using given tg.UserClass.

func (*Peer) Keys added in v0.3.0

func (p *Peer) Keys() []string

Keys returns list of all associated keys (phones, usernames, etc.) stored in the peer.

func (Peer) Marshal added in v0.16.0

func (p Peer) Marshal(e *jx.Encoder) error

func (Peer) MarshalJSON added in v0.16.0

func (p Peer) MarshalJSON() ([]byte, error)

func (Peer) String added in v0.16.0

func (p Peer) String() string

func (*Peer) Unmarshal added in v0.16.0

func (p *Peer) Unmarshal(d *jx.Decoder) error

func (*Peer) UnmarshalJSON added in v0.16.0

func (p *Peer) UnmarshalJSON(data []byte) error

type PeerCollector added in v0.5.0

type PeerCollector struct {
	// contains filtered or unexported fields
}

PeerCollector is a simple helper to collect peers from different sources.

func CollectPeers added in v0.5.0

func CollectPeers(storage PeerStorage) PeerCollector

CollectPeers creates new PeerCollector.

Example
package main

import (
	"context"
	"fmt"
	"os"
	"os/signal"

	pebbledb "github.com/cockroachdb/pebble"
	"github.com/go-faster/errors"

	"github.com/gotd/td/telegram"
	"github.com/gotd/td/telegram/query"
	"github.com/gotd/td/tg"

	"github.com/gotd/contrib/pebble"
	"github.com/gotd/contrib/storage"
)

func peerCollector(ctx context.Context) error {
	db, err := pebbledb.Open("pebble.db", &pebbledb.Options{})
	if err != nil {
		return errors.Errorf("create pebble storage: %w", err)
	}
	s := pebble.NewPeerStorage(db)
	collector := storage.CollectPeers(s)

	client, err := telegram.ClientFromEnvironment(telegram.Options{})
	if err != nil {
		return errors.Errorf("create client: %w", err)
	}
	raw := tg.NewClient(client)

	return client.Run(ctx, func(ctx context.Context) error {
		// Fills storage with user dialogs peers metadata.
		return collector.Dialogs(ctx, query.GetDialogs(raw).Iter())
	})
}

func main() {
	ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)
	defer cancel()

	if err := peerCollector(ctx); err != nil {
		_, _ = fmt.Fprintf(os.Stderr, "%+v\n", err)
		os.Exit(1)
	}
}
Output:

func (PeerCollector) Contacts added in v0.5.0

func (c PeerCollector) Contacts(ctx context.Context, contacts *tg.ContactsContacts) error

Contacts collects peers from contacts iterator.

func (PeerCollector) Dialogs added in v0.5.0

func (c PeerCollector) Dialogs(ctx context.Context, iter *dialogs.Iterator) error

Dialogs collects peers from dialog iterator.

func (PeerCollector) Participants added in v0.5.0

func (c PeerCollector) Participants(ctx context.Context, iter *participants.Iterator) error

Participants collects peers from participants iterator.

type PeerIterator added in v0.5.0

type PeerIterator interface {
	Next(ctx context.Context) bool
	Err() error
	Value() Peer
	io.Closer
}

PeerIterator is a peer iterator.

type PeerKey added in v0.5.0

type PeerKey struct {
	Kind dialogs.PeerKind
	ID   int64
}

PeerKey is unique key of peer object.

func KeyFromPeer

func KeyFromPeer(p Peer) PeerKey

KeyFromPeer creates key from peer.

func (PeerKey) Bytes added in v0.5.0

func (k PeerKey) Bytes(r []byte) []byte

Bytes returns bytes representation of key.

func (*PeerKey) Parse added in v0.5.0

func (k *PeerKey) Parse(r []byte) error

Parse parses bytes representation from given slice.

func (PeerKey) String added in v0.5.0

func (k PeerKey) String() string

String returns string representation of key.

type PeerStorage

type PeerStorage interface {
	// Add adds given peer to the storage.
	Add(ctx context.Context, value Peer) error
	// Find finds peer using given key.
	// If peer not found, it returns ErrPeerNotFound error.
	Find(ctx context.Context, key PeerKey) (Peer, error)

	// Assign adds given peer to the storage and associates it to the given key.
	Assign(ctx context.Context, key string, value Peer) error
	// Resolve finds peer using associated key.
	// If peer not found, it returns ErrPeerNotFound error.
	Resolve(ctx context.Context, key string) (Peer, error)

	// Iterate creates and returns new PeerIterator.
	Iterate(ctx context.Context) (PeerIterator, error)
}

PeerStorage is abstraction for peer storage.

type ResolverCache

type ResolverCache struct {
	// contains filtered or unexported fields
}

ResolverCache is a peer.Resolver cache implemented using peer storage.

Example
package main

import (
	"context"
	"fmt"
	"os"
	"os/signal"

	pebbledb "github.com/cockroachdb/pebble"
	"github.com/go-faster/errors"

	"github.com/gotd/td/telegram/message"
	"github.com/gotd/td/telegram/message/peer"
	"github.com/gotd/td/tg"

	"github.com/gotd/td/telegram"

	"github.com/gotd/contrib/pebble"
	"github.com/gotd/contrib/storage"
)

func resolverCache(ctx context.Context) error {
	db, err := pebbledb.Open("pebble.db", &pebbledb.Options{})
	if err != nil {
		return errors.Errorf("create pebble storage: %w", err)
	}

	client, err := telegram.ClientFromEnvironment(telegram.Options{})
	if err != nil {
		return errors.Errorf("create client: %w", err)
	}

	return client.Run(ctx, func(ctx context.Context) error {
		raw := tg.NewClient(client)
		resolver := storage.NewResolverCache(peer.Plain(raw), pebble.NewPeerStorage(db))
		s := message.NewSender(raw).WithResolver(resolver)

		_, err := s.Resolve("durov").Text(ctx, "Hi!")
		return err
	})
}

func main() {
	ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)
	defer cancel()

	if err := resolverCache(ctx); err != nil {
		_, _ = fmt.Fprintf(os.Stderr, "%+v\n", err)
		os.Exit(1)
	}
}
Output:

func NewResolverCache

func NewResolverCache(next peer.Resolver, storage PeerStorage) ResolverCache

NewResolverCache creates new ResolverCache.

func (ResolverCache) ResolveDomain

func (r ResolverCache) ResolveDomain(ctx context.Context, domain string) (tg.InputPeerClass, error)

ResolveDomain implements peer.Resolver

func (ResolverCache) ResolvePhone

func (r ResolverCache) ResolvePhone(ctx context.Context, phone string) (tg.InputPeerClass, error)

ResolvePhone implements peer.Resolver

Jump to

Keyboard shortcuts

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