sync: add on-command sync
Signed-off-by: Sumner Evans <sumner.evans@automattic.com>
This commit is contained in:
@@ -0,0 +1,134 @@
|
||||
package connector
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math"
|
||||
|
||||
"github.com/gotd/td/tg"
|
||||
"github.com/rs/zerolog"
|
||||
"maunium.net/go/mautrix/bridgev2"
|
||||
"maunium.net/go/mautrix/bridgev2/database"
|
||||
"maunium.net/go/mautrix/bridgev2/simplevent"
|
||||
|
||||
"go.mau.fi/mautrix-telegram/pkg/connector/ids"
|
||||
)
|
||||
|
||||
func (t *TelegramClient) SyncChats(ctx context.Context) error {
|
||||
log := zerolog.Ctx(ctx)
|
||||
|
||||
limit := t.main.Config.Sync.UpdateLimit
|
||||
if limit <= 0 {
|
||||
limit = math.MaxInt32
|
||||
}
|
||||
|
||||
dialogs, err := APICallWithUpdates(ctx, t, func() (tg.ModifiedMessagesDialogs, error) {
|
||||
d, err := t.client.API().MessagesGetDialogs(ctx, &tg.MessagesGetDialogsRequest{
|
||||
Limit: limit,
|
||||
OffsetPeer: &tg.InputPeerEmpty{},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if dialogs, ok := d.(tg.ModifiedMessagesDialogs); !ok {
|
||||
return nil, fmt.Errorf("unexpected dialogs type %T", d)
|
||||
} else {
|
||||
return dialogs, nil
|
||||
}
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var created int
|
||||
for _, d := range dialogs.GetDialogs() {
|
||||
if d.TypeID() != tg.DialogTypeID {
|
||||
continue
|
||||
}
|
||||
dialog := d.(*tg.Dialog)
|
||||
|
||||
log := log.With().
|
||||
Stringer("peer", dialog.Peer).
|
||||
Int("top_message", dialog.TopMessage).
|
||||
Logger()
|
||||
|
||||
portalKey := ids.MakePortalKey(dialog.GetPeer(), t.loginID)
|
||||
portal, err := t.main.Bridge.GetPortalByKey(ctx, portalKey)
|
||||
if err != nil {
|
||||
log.Err(err).Msg("Failed to get portal")
|
||||
continue
|
||||
}
|
||||
|
||||
// TODO make sure that the user isn't deleted.
|
||||
|
||||
if portal == nil || portal.MXID == "" {
|
||||
// Check what the latest message is
|
||||
messages, err := APICallWithUpdates(ctx, t, func() (tg.ModifiedMessagesMessages, error) {
|
||||
inputMessages := []tg.InputMessageClass{
|
||||
&tg.InputMessageID{ID: dialog.TopMessage},
|
||||
}
|
||||
var msgs tg.MessagesMessagesClass
|
||||
switch v := dialog.Peer.(type) {
|
||||
case *tg.PeerUser, *tg.PeerChat:
|
||||
msgs, err = t.client.API().MessagesGetMessages(ctx, inputMessages)
|
||||
case *tg.PeerChannel:
|
||||
var accessHash int64
|
||||
var found bool
|
||||
accessHash, found, err = t.ScopedStore.GetChannelAccessHash(ctx, t.telegramUserID, v.ChannelID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get channel access hash: %w", err)
|
||||
} else if !found {
|
||||
return nil, fmt.Errorf("channel access hash for %d not found", v.ChannelID)
|
||||
} else {
|
||||
msgs, err = t.client.API().ChannelsGetMessages(ctx, &tg.ChannelsGetMessagesRequest{
|
||||
Channel: &tg.InputChannel{ChannelID: v.ChannelID, AccessHash: accessHash},
|
||||
ID: inputMessages,
|
||||
})
|
||||
}
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown peer type %T", dialog.Peer)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if messages, ok := msgs.(tg.ModifiedMessagesMessages); !ok {
|
||||
return nil, fmt.Errorf("unsupported messages type %T", messages)
|
||||
} else {
|
||||
return messages, nil
|
||||
}
|
||||
})
|
||||
if err != nil {
|
||||
log.Err(err).Msg("Failed to get latest message for portal")
|
||||
continue
|
||||
} else if len(messages.GetMessages()) == 0 {
|
||||
log.Warn().Msg("No messages found for portal")
|
||||
continue
|
||||
}
|
||||
topMessage := messages.GetMessages()[0]
|
||||
if topMessage.TypeID() == tg.MessageServiceTypeID {
|
||||
action := topMessage.(*tg.MessageService).Action
|
||||
if action.TypeID() == tg.MessageActionContactSignUpTypeID || action.TypeID() == tg.MessageActionHistoryClearTypeID {
|
||||
log.Debug().Str("action_type", action.TypeName()).Msg("Not syncing portal because it's a contact sign up or history clear")
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
created++ // The portal will have to be created
|
||||
if created > t.main.Config.Sync.CreateLimit {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// TODO use the bundled backfill data?
|
||||
t.main.Bridge.QueueRemoteEvent(t.userLogin, &simplevent.ChatResync{
|
||||
EventMeta: simplevent.EventMeta{
|
||||
Type: bridgev2.RemoteEventChatResync,
|
||||
LogContext: func(c zerolog.Context) zerolog.Context {
|
||||
return c.Str("update", "sync")
|
||||
},
|
||||
PortalKey: portalKey,
|
||||
CreatePortal: true,
|
||||
},
|
||||
CheckNeedsBackfillFunc: func(ctx context.Context, latestMessage *database.Message) (bool, error) { return true, nil },
|
||||
})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user