storage

package
v0.0.0-...-d21987e Latest Latest
Warning

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

Go to latest
Published: Sep 7, 2023 License: MIT Imports: 16 Imported by: 0

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

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

ForEach calls callback on every iterator element.

func UpdateHook

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/t2bot/gotd-contrib/pebble"
	"github.com/t2bot/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

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

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

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

FromUser fills Peer object using given tg.UserClass.

func (*Peer) Keys

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

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

func (Peer) Marshal

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

func (Peer) MarshalJSON

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

func (Peer) String

func (p Peer) String() string

func (*Peer) Unmarshal

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

func (*Peer) UnmarshalJSON

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

type PeerCollector

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

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

func CollectPeers

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/t2bot/gotd-contrib/pebble"
	"github.com/t2bot/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

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

Contacts collects peers from contacts iterator.

func (PeerCollector) Dialogs

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

Dialogs collects peers from dialog iterator.

func (PeerCollector) Participants

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

Participants collects peers from participants iterator.

type PeerIterator

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

PeerIterator is a peer iterator.

type PeerKey

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

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

Bytes returns bytes representation of key.

func (*PeerKey) Parse

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

Parse parses bytes representation from given slice.

func (PeerKey) String

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/t2bot/gotd-contrib/pebble"
	"github.com/t2bot/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