7a04f298d2
- update to latest telegram layer - remove some references to fields in tg.Entities that don't exist in the schema - originally added here: https://github.com/beeper/td/commit/820929062a2ba0104397bc01235ab58a9cff780e - referenced here - https://github.com/mautrix/telegramgo/commit/124f0967ed195b5a380c9bd02e170ada9710dde3 - https://github.com/mautrix/telegramgo/commit/4205047aab2e0639217148b5d125bfaab668bd8e
236 lines
5.5 KiB
Go
236 lines
5.5 KiB
Go
package peers
|
|
|
|
import (
|
|
"context"
|
|
|
|
"github.com/go-faster/errors"
|
|
"go.uber.org/multierr"
|
|
|
|
"go.mau.fi/mautrix-telegram/pkg/gotd/constant"
|
|
"go.mau.fi/mautrix-telegram/pkg/gotd/tg"
|
|
)
|
|
|
|
func (m *Manager) applyUsers(ctx context.Context, input ...tg.UserClass) error {
|
|
var (
|
|
users []*tg.User
|
|
ids = make([]constant.TDLibPeerID, 0, 16)
|
|
)
|
|
if len(ids) < len(input) {
|
|
ids = make([]constant.TDLibPeerID, 0, len(input))
|
|
}
|
|
|
|
for _, user := range input {
|
|
user, ok := user.(*tg.User)
|
|
if !ok {
|
|
// Got nil or Empty.
|
|
continue
|
|
}
|
|
if user.Min {
|
|
// TODO(tdakkota): call some hook to get actual user if got min (e.g. force gaps to getDifference)
|
|
continue
|
|
}
|
|
users = append(users, user)
|
|
|
|
id := user.GetID()
|
|
k := Key{
|
|
Prefix: usersPrefix,
|
|
ID: id,
|
|
}
|
|
v := Value{
|
|
AccessHash: user.AccessHash,
|
|
}
|
|
if err := m.storage.Save(ctx, k, v); err != nil {
|
|
// FIXME(tdakkota): just log errors?
|
|
return errors.Wrapf(err, "save user %d", user.ID)
|
|
}
|
|
if user.Phone != "" {
|
|
if err := m.storage.SavePhone(ctx, user.Phone, k); err != nil {
|
|
return errors.Wrapf(err, "save user %d", user.ID)
|
|
}
|
|
}
|
|
ids = append(ids, userPeerID(id))
|
|
}
|
|
|
|
if err := m.cache.SaveUsers(ctx, users...); err != nil {
|
|
return errors.Wrap(err, "cache users")
|
|
}
|
|
m.updated(ids...)
|
|
return nil
|
|
}
|
|
|
|
func (m *Manager) applyChats(ctx context.Context, input ...tg.ChatClass) error {
|
|
var (
|
|
chats []*tg.Chat
|
|
channels []*tg.Channel
|
|
ids = make([]constant.TDLibPeerID, 0, 16)
|
|
)
|
|
if len(ids) < len(input) {
|
|
ids = make([]constant.TDLibPeerID, 0, len(input))
|
|
}
|
|
|
|
for _, ch := range input {
|
|
var (
|
|
k Key
|
|
v Value
|
|
)
|
|
// FIXME(tdakkota): check min constructors
|
|
switch ch := ch.(type) {
|
|
case *tg.Chat:
|
|
k.ID = ch.ID
|
|
k.Prefix = chatsPrefix
|
|
chats = append(chats, ch)
|
|
|
|
ids = append(ids, chatPeerID(ch.ID))
|
|
case *tg.ChatForbidden:
|
|
k.ID = ch.ID
|
|
k.Prefix = chatsPrefix
|
|
case *tg.Channel:
|
|
k.ID = ch.ID
|
|
v.AccessHash = ch.AccessHash
|
|
k.Prefix = channelPrefix
|
|
channels = append(channels, ch)
|
|
|
|
ids = append(ids, channelPeerID(ch.ID))
|
|
case *tg.ChannelForbidden:
|
|
k.ID = ch.ID
|
|
v.AccessHash = ch.AccessHash
|
|
k.Prefix = channelPrefix
|
|
default:
|
|
// Got nil or Empty
|
|
continue
|
|
}
|
|
|
|
if err := m.storage.Save(ctx, k, v); err != nil {
|
|
// FIXME(tdakkota): just log errors?
|
|
return errors.Wrapf(err, "save chat %d", k.ID)
|
|
}
|
|
}
|
|
|
|
if err := m.cache.SaveChats(ctx, chats...); err != nil {
|
|
return errors.Wrap(err, "cache chats")
|
|
}
|
|
if err := m.cache.SaveChannels(ctx, channels...); err != nil {
|
|
return errors.Wrap(err, "cache channels")
|
|
}
|
|
m.updated(ids...)
|
|
return nil
|
|
}
|
|
|
|
// Apply adds given entities to manager state.
|
|
func (m *Manager) Apply(ctx context.Context, users []tg.UserClass, chats []tg.ChatClass) error {
|
|
return m.applyEntities(ctx, users, chats)
|
|
}
|
|
|
|
func (m *Manager) applyEntities(ctx context.Context, users []tg.UserClass, chats []tg.ChatClass) error {
|
|
return multierr.Append(m.applyUsers(ctx, users...), m.applyChats(ctx, chats...))
|
|
}
|
|
|
|
func (m *Manager) applyFullUser(ctx context.Context, user *tg.UserFull) error {
|
|
if user == nil {
|
|
return nil
|
|
}
|
|
m.updatedFull(userPeerID(user.ID))
|
|
return m.cache.SaveUserFulls(ctx, user)
|
|
}
|
|
|
|
func (m *Manager) applyFullChat(ctx context.Context, chat *tg.ChatFull) error {
|
|
if chat == nil {
|
|
return nil
|
|
}
|
|
m.updatedFull(chatPeerID(chat.ID))
|
|
return m.cache.SaveChatFulls(ctx, chat)
|
|
}
|
|
|
|
func (m *Manager) applyFullChannel(ctx context.Context, ch *tg.ChannelFull) error {
|
|
if ch == nil {
|
|
return nil
|
|
}
|
|
m.updatedFull(channelPeerID(ch.ID))
|
|
return m.cache.SaveChannelFulls(ctx, ch)
|
|
}
|
|
|
|
func (m *Manager) updateContacts(ctx context.Context) ([]tg.UserClass, error) {
|
|
ch := m.sg.DoChan("_contacts", func() (interface{}, error) {
|
|
hash, err := m.storage.GetContactsHash(ctx)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "get contacts hash")
|
|
}
|
|
|
|
r, err := m.api.ContactsGetContacts(ctx, hash)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "get contacts")
|
|
}
|
|
|
|
switch c := r.(type) {
|
|
case *tg.ContactsContacts:
|
|
if err := m.applyUsers(ctx, c.Users...); err != nil {
|
|
return nil, errors.Wrap(err, "update users")
|
|
}
|
|
|
|
myID, ok := m.myID()
|
|
if !ok {
|
|
return c.Users, nil
|
|
}
|
|
|
|
if err := m.storage.SaveContactsHash(ctx, contactsHash(myID, c)); err != nil {
|
|
return nil, errors.Wrap(err, "update contacts hash")
|
|
}
|
|
return c.Users, nil
|
|
case *tg.ContactsContactsNotModified:
|
|
return nil, nil
|
|
default:
|
|
return nil, errors.Errorf("unexpected type %T", r)
|
|
}
|
|
})
|
|
|
|
select {
|
|
case r := <-ch:
|
|
if err := r.Err; err != nil {
|
|
return nil, err
|
|
}
|
|
users, ok := r.Val.([]tg.UserClass)
|
|
if !ok {
|
|
return nil, nil
|
|
}
|
|
return users, nil
|
|
case <-ctx.Done():
|
|
return nil, ctx.Err()
|
|
}
|
|
}
|
|
|
|
type vectorHash struct {
|
|
state uint64
|
|
}
|
|
|
|
// See https://github.com/tdlib/td/blob/aa8a4979df8fc56032f134471a2cb939a7b0839f/td/telegram/misc.cpp#L242.
|
|
func (h *vectorHash) apply(n uint64) {
|
|
h.state ^= h.state >> 21
|
|
h.state ^= h.state << 35
|
|
h.state ^= h.state >> 4
|
|
h.state += n
|
|
}
|
|
|
|
// See https://github.com/tdlib/td/blob/aa8a4979df8fc56032f134471a2cb939a7b0839f/td/telegram/ContactsManager.cpp#L5125.
|
|
func contactsHash(myID int64, contacts *tg.ContactsContacts) int64 {
|
|
contacts.MapUsers().SortStableByID()
|
|
|
|
var lesserIDx = len(contacts.Users) - 1
|
|
for i, user := range contacts.Users {
|
|
if user.GetID() < myID {
|
|
lesserIDx = i
|
|
break
|
|
}
|
|
}
|
|
|
|
var h vectorHash
|
|
h.apply(uint64(len(contacts.Users)))
|
|
for i, contact := range contacts.Users {
|
|
h.apply(uint64(contact.GetID()))
|
|
if i == lesserIDx {
|
|
h.apply(uint64(myID))
|
|
}
|
|
}
|
|
|
|
return int64(h.state)
|
|
}
|