diff --git a/pkg/connector/backfill.go b/pkg/connector/backfill.go index f7c27ea3..22e8c68f 100644 --- a/pkg/connector/backfill.go +++ b/pkg/connector/backfill.go @@ -110,6 +110,14 @@ func (t *TelegramClient) takeoutDialogs(ctx context.Context, takeoutID int64) er return nil } + if req.OffsetPeer.TypeID() == tg.InputPeerEmptyTypeID { + // This is the first fetch of dialogs, reset the pinned dialogs + // based on the list. + if err := t.resetPinnedDialogs(ctx, dialogs.GetDialogs()); err != nil { + return err + } + } + err = t.handleDialogs(ctx, dialogs, -1) if err != nil { return fmt.Errorf("failed to handle dialogs: %w", err) diff --git a/pkg/connector/client.go b/pkg/connector/client.go index 0d7bfa09..a73cf995 100644 --- a/pkg/connector/client.go +++ b/pkg/connector/client.go @@ -183,6 +183,9 @@ func NewTelegramClient(ctx context.Context, tc *TelegramConnector, login *bridge dispatcher.OnNotifySettings(func(ctx context.Context, e tg.Entities, update *tg.UpdateNotifySettings) error { return client.onNotifySettings(ctx, update) }) + dispatcher.OnPinnedDialogs(func(ctx context.Context, e tg.Entities, update *tg.UpdatePinnedDialogs) error { + return client.onPinnedDialogs(ctx, update) + }) client.ScopedStore = tc.Store.GetScopedStore(telegramUserID) diff --git a/pkg/connector/config.go b/pkg/connector/config.go index e1f9d163..6634fd2b 100644 --- a/pkg/connector/config.go +++ b/pkg/connector/config.go @@ -168,6 +168,8 @@ type UserLoginMetadata struct { TakeoutDialogCrawlDone bool `json:"takeout_portal_crawl_done,omitempty"` TakeoutDialogCrawlCursor networkid.PortalID `json:"takeout_portal_crawl_cursor,omitempty"` + + PinnedDialogs []networkid.PortalID `json:"pinned_dialogs,omitempty"` } func (s *UserLoginSession) Load(_ context.Context) (*session.Data, error) { diff --git a/pkg/connector/sync.go b/pkg/connector/sync.go index 7b14face..f8238485 100644 --- a/pkg/connector/sync.go +++ b/pkg/connector/sync.go @@ -13,6 +13,7 @@ import ( "maunium.net/go/mautrix/bridgev2/database" "maunium.net/go/mautrix/bridgev2/networkid" "maunium.net/go/mautrix/bridgev2/simplevent" + "maunium.net/go/mautrix/event" "go.mau.fi/mautrix-telegram/pkg/connector/ids" ) @@ -40,9 +41,24 @@ func (t *TelegramClient) SyncChats(ctx context.Context) error { return err } + if err := t.resetPinnedDialogs(ctx, dialogs.GetDialogs()); err != nil { + return err + } + return t.handleDialogs(ctx, dialogs, t.main.Config.Sync.CreateLimit) } +func (t *TelegramClient) resetPinnedDialogs(ctx context.Context, dialogs []tg.DialogClass) error { + t.userLogin.Metadata.(*UserLoginMetadata).PinnedDialogs = nil + for _, dialog := range dialogs { + if dialog.GetPinned() { + portalKey := t.makePortalKeyFromPeer(dialog.GetPeer()) + t.userLogin.Metadata.(*UserLoginMetadata).PinnedDialogs = append(t.userLogin.Metadata.(*UserLoginMetadata).PinnedDialogs, portalKey.ID) + } + } + return t.userLogin.Save(ctx) +} + func (t *TelegramClient) handleDialogs(ctx context.Context, dialogs tg.ModifiedMessagesDialogs, createLimit int) error { log := zerolog.Ctx(ctx) @@ -106,6 +122,9 @@ func (t *TelegramClient) handleDialogs(ctx context.Context, dialogs tg.ModifiedM } else { userLocalInfo.MutedUntil = &bridgev2.Unmuted } + if dialog.Pinned { + userLocalInfo.Tag = ptr.Ptr(event.RoomTagFavourite) + } t.main.Bridge.QueueRemoteEvent(t.userLogin, &simplevent.ChatResync{ ChatInfo: &bridgev2.ChatInfo{Name: &portal.Name, UserLocal: &userLocalInfo}, diff --git a/pkg/connector/telegram.go b/pkg/connector/telegram.go index 8526ef75..55f20873 100644 --- a/pkg/connector/telegram.go +++ b/pkg/connector/telegram.go @@ -758,6 +758,53 @@ func (t *TelegramClient) HandleMute(ctx context.Context, msg *bridgev2.MatrixMut return err } +func (t *TelegramClient) onPinnedDialogs(ctx context.Context, msg *tg.UpdatePinnedDialogs) error { + needsUnpinning := map[networkid.PortalKey]struct{}{} + for _, portalID := range t.userLogin.Metadata.(*UserLoginMetadata).PinnedDialogs { + pt, id, err := ids.ParsePortalID(portalID) + if err != nil { + return err + } + needsUnpinning[t.makePortalKeyFromID(pt, id)] = struct{}{} + } + t.userLogin.Metadata.(*UserLoginMetadata).PinnedDialogs = nil + + for _, d := range msg.Order { + dialog, ok := d.(*tg.DialogPeer) + if !ok { + continue + } + portalKey := t.makePortalKeyFromPeer(dialog.Peer) + delete(needsUnpinning, portalKey) + t.userLogin.Metadata.(*UserLoginMetadata).PinnedDialogs = append(t.userLogin.Metadata.(*UserLoginMetadata).PinnedDialogs, portalKey.ID) + + t.main.Bridge.QueueRemoteEvent(t.userLogin, &simplevent.ChatResync{ + ChatInfo: &bridgev2.ChatInfo{UserLocal: &bridgev2.UserLocalPortalInfo{ + Tag: ptr.Ptr(event.RoomTagFavourite), + }}, + EventMeta: simplevent.EventMeta{ + Type: bridgev2.RemoteEventChatResync, + PortalKey: portalKey, + }, + }) + } + + var empty event.RoomTag + for portalKey := range needsUnpinning { + t.main.Bridge.QueueRemoteEvent(t.userLogin, &simplevent.ChatResync{ + ChatInfo: &bridgev2.ChatInfo{UserLocal: &bridgev2.UserLocalPortalInfo{ + Tag: &empty, + }}, + EventMeta: simplevent.EventMeta{ + Type: bridgev2.RemoteEventChatResync, + PortalKey: portalKey, + }, + }) + } + + return t.userLogin.Save(ctx) +} + func (t *TelegramClient) HandleRoomTag(ctx context.Context, msg *bridgev2.MatrixRoomTag) error { inputPeer, err := t.inputPeerForPortalID(ctx, msg.Portal.ID) if err != nil {