From 60fe2e07c2a4183757e71d95057bb77fee767daa Mon Sep 17 00:00:00 2001 From: Sumner Evans Date: Wed, 28 Aug 2024 10:50:22 -0600 Subject: [PATCH] bridge state: set remote name and profile Signed-off-by: Sumner Evans --- go.mod | 2 +- go.sum | 4 +- pkg/connector/api.go | 2 +- pkg/connector/client.go | 12 +++++- pkg/connector/commands.go | 6 +-- pkg/connector/login.go | 27 +++++++++++- pkg/connector/telegram.go | 87 ++++++++++++++++++++++++++++++++------- 7 files changed, 114 insertions(+), 26 deletions(-) diff --git a/go.mod b/go.mod index 0a775f05..6af3aea4 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( go.uber.org/zap v1.27.0 golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa golang.org/x/net v0.28.0 - maunium.net/go/mautrix v0.20.1-0.20240827221252-892e5cf01fc8 + maunium.net/go/mautrix v0.20.1-0.20240828181422-5f49ca683a3d ) require ( diff --git a/go.sum b/go.sum index 5e597ea0..296f648a 100644 --- a/go.sum +++ b/go.sum @@ -116,8 +116,8 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= maunium.net/go/mauflag v1.0.0 h1:YiaRc0tEI3toYtJMRIfjP+jklH45uDHtT80nUamyD4M= maunium.net/go/mauflag v1.0.0/go.mod h1:nLivPOpTpHnpzEh8jEdSL9UqO9+/KBJFmNRlwKfkPeA= -maunium.net/go/mautrix v0.20.1-0.20240827221252-892e5cf01fc8 h1:k+HkbFrExb508ofb+Snz+yGa1/GQ6WKqyGzGYXSzT3A= -maunium.net/go/mautrix v0.20.1-0.20240827221252-892e5cf01fc8/go.mod h1:7hh/Hx5W9lUcqL0hkSw52kMyY+6nMUPTtdDN0qVEXwI= +maunium.net/go/mautrix v0.20.1-0.20240828181422-5f49ca683a3d h1:JMsmZmi6NnlE0Y6bC81UHCCRLfzovpPZWzI6dDetU54= +maunium.net/go/mautrix v0.20.1-0.20240828181422-5f49ca683a3d/go.mod h1:7hh/Hx5W9lUcqL0hkSw52kMyY+6nMUPTtdDN0qVEXwI= nhooyr.io/websocket v1.8.11 h1:f/qXNc2/3DpoSZkHt1DQu6rj4zGC8JmkkLkWss0MgN0= nhooyr.io/websocket v1.8.11/go.mod h1:rN9OFWIUwuxg4fR5tELlYC04bXYowCP9GX47ivo2l+c= rsc.io/qr v0.2.0 h1:6vBLea5/NRMVTz8V66gipeLycZMl/+UlFmk8DvqQ6WY= diff --git a/pkg/connector/api.go b/pkg/connector/api.go index 514aecef..4f3cbde9 100644 --- a/pkg/connector/api.go +++ b/pkg/connector/api.go @@ -24,7 +24,7 @@ func APICallWithUpdates[U hasUpdates](ctx context.Context, t *TelegramClient, fn if !ok { return resp, fmt.Errorf("user is %T not *tg.User", user) } - err := t.updateGhost(ctx, user.ID, user) + _, err := t.updateGhost(ctx, user.ID, user) if err != nil { return resp, err } diff --git a/pkg/connector/client.go b/pkg/connector/client.go index 8d2c9b11..eb81da2c 100644 --- a/pkg/connector/client.go +++ b/pkg/connector/client.go @@ -393,6 +393,14 @@ func (t *TelegramClient) Connect(ctx context.Context) (err error) { t.clientCancel() } }() + + // Update the logged-in user's ghost info (this also updates the user + // login's remote name and profile). + if me, err := t.client.Self(ctx); err != nil { + return fmt.Errorf("failed to get self: %w", err) + } else if _, err := t.updateGhost(ctx, t.telegramUserID, me); err != nil { + return fmt.Errorf("failed to update ghost: %w", err) + } return } @@ -427,10 +435,10 @@ func (t *TelegramClient) GetUserInfo(ctx context.Context, ghost *bridgev2.Ghost) } if user, err := t.getSingleUser(ctx, id); err != nil { return nil, fmt.Errorf("failed to get user %d: %w", id, err) - } else if userInfo, err := t.getUserInfoFromTelegramUser(ctx, user); err != nil { + } else if user.TypeID() != tg.UserTypeID { return nil, err } else { - return userInfo, t.updateGhostWithUserInfo(ctx, id, userInfo) + return t.updateGhost(ctx, id, user.(*tg.User)) } } diff --git a/pkg/connector/commands.go b/pkg/connector/commands.go index 823f6a1f..e53deedd 100644 --- a/pkg/connector/commands.go +++ b/pkg/connector/commands.go @@ -60,9 +60,9 @@ func fnSync(ce *commands.Event) { ce.Reply("Failed to get your info for %s: %v", login.ID, err) } else if len(users) == 0 { ce.Reply("Failed to get your info for %s: no users returned", login.ID) - } else if userInfo, err := client.getUserInfoFromTelegramUser(ce.Ctx, users[0]); err != nil { - ce.Reply("Failed to get your info for %s: %v", login.ID, err) - } else if err = client.updateGhostWithUserInfo(ce.Ctx, client.telegramUserID, userInfo); err != nil { + } else if users[0].TypeID() != tg.UserTypeID { + ce.Reply("Unexpected user type %s", users[0].TypeName()) + } else if _, err = client.updateGhost(ce.Ctx, client.telegramUserID, users[0].(*tg.User)); err != nil { ce.Reply("Failed to update your info for %s: %v", login.ID, err) } }() diff --git a/pkg/connector/login.go b/pkg/connector/login.go index ce0308df..7b2ed2cf 100644 --- a/pkg/connector/login.go +++ b/pkg/connector/login.go @@ -21,6 +21,7 @@ import ( "fmt" "github.com/gotd/td/tg" + "maunium.net/go/mautrix/bridge/status" "maunium.net/go/mautrix/bridgev2" "maunium.net/go/mautrix/bridgev2/database" @@ -85,10 +86,34 @@ func finalizeLogin(ctx context.Context, user *bridgev2.User, authorization *tg.A log.Err(err).Msg("Failed to sync chats") } }() + + fullName := util.FormatFullName(me.FirstName, me.LastName) + username := me.Username + if username == "" && len(me.Usernames) > 0 { + username = me.Usernames[0].Username + } + remoteName := username + if remoteName == "" { + remoteName = me.Phone + } + if remoteName == "" { + remoteName = fullName + } + ul.RemoteName = remoteName + ul.RemoteProfile = status.RemoteProfile{ + Phone: me.Phone, + Username: username, + Name: fullName, + } + err = ul.Save(ctx) + if err != nil { + return nil, fmt.Errorf("failed to save login: %w", err) + } + return &bridgev2.LoginStep{ Type: bridgev2.LoginStepTypeComplete, StepID: LoginStepIDComplete, - Instructions: fmt.Sprintf("Successfully logged in as %d / +%s (%s)", me.ID, me.Phone, util.FormatFullName(me.FirstName, me.LastName)), + Instructions: fmt.Sprintf("Successfully logged in as %d / +%s (%s)", me.ID, me.Phone, remoteName), CompleteParams: &bridgev2.LoginCompleteParams{ UserLoginID: ul.ID, }, diff --git a/pkg/connector/telegram.go b/pkg/connector/telegram.go index f817f125..994b180f 100644 --- a/pkg/connector/telegram.go +++ b/pkg/connector/telegram.go @@ -4,10 +4,13 @@ import ( "bytes" "context" "fmt" + "slices" + "strings" "time" "github.com/gotd/td/tg" "github.com/rs/zerolog" + "maunium.net/go/mautrix/bridge/status" "maunium.net/go/mautrix/bridgev2" "maunium.net/go/mautrix/bridgev2/database" "maunium.net/go/mautrix/bridgev2/networkid" @@ -194,6 +197,52 @@ func (t *TelegramClient) getEventSender(msg interface { } } +func (t *TelegramClient) maybeUpdateRemoteProfile(ctx context.Context, ghost *bridgev2.Ghost, user *tg.User) error { + if ghost.ID != t.userID { + return nil + } + + var changed bool + if user != nil { + fullName := util.FormatFullName(user.FirstName, user.LastName) + username := user.Username + if username == "" && len(user.Usernames) > 0 { + username = user.Usernames[0].Username + } + + normalizedPhone := "+" + strings.TrimPrefix(user.Phone, "+") + remoteName := username + if remoteName == "" { + remoteName = normalizedPhone + } + if remoteName == "" { + remoteName = fullName + } + + changed = t.userLogin.RemoteName != remoteName || + t.userLogin.RemoteProfile.Phone != normalizedPhone || + t.userLogin.RemoteProfile.Username != username || + t.userLogin.RemoteProfile.Name != fullName + t.userLogin.RemoteName = remoteName + t.userLogin.RemoteProfile.Phone = normalizedPhone + t.userLogin.RemoteProfile.Username = username + t.userLogin.RemoteProfile.Name = fullName + } else { + changed = t.userLogin.RemoteName != ghost.Name + t.userLogin.RemoteProfile.Name = ghost.Name + } + + changed = changed || t.userLogin.RemoteProfile.Avatar != ghost.AvatarMXC + t.userLogin.RemoteProfile.Avatar = ghost.AvatarMXC + if changed { + if err := t.userLogin.Save(ctx); err != nil { + return err + } + t.userLogin.BridgeState.Send(status.BridgeState{StateEvent: status.StateConnected}) + } + return nil +} + func (t *TelegramClient) onUserName(ctx context.Context, e tg.Entities, update *tg.UpdateUserName) error { ghost, err := t.main.Bridge.GetGhostByID(ctx, ids.MakeUserID(update.UserID)) if err != nil { @@ -201,10 +250,25 @@ func (t *TelegramClient) onUserName(ctx context.Context, e tg.Entities, update * } name := util.FormatFullName(update.FirstName, update.LastName) + userInfo := bridgev2.UserInfo{Name: &name} - // TODO update identifiers? - ghost.UpdateInfo(ctx, &bridgev2.UserInfo{Name: &name}) - return nil + if len(update.Usernames) > 0 { + for _, ident := range ghost.Identifiers { + if !strings.HasPrefix(ident, "telegram:") { + userInfo.Identifiers = append(userInfo.Identifiers, ident) + } + } + + for _, username := range update.Usernames { + userInfo.Identifiers = append(userInfo.Identifiers, fmt.Sprintf("telegram:%s", username.Username)) + } + + slices.Sort(userInfo.Identifiers) + userInfo.Identifiers = slices.Compact(userInfo.Identifiers) + } + + ghost.UpdateInfo(ctx, &userInfo) + return t.maybeUpdateRemoteProfile(ctx, ghost, nil) } func (t *TelegramClient) onDeleteMessages(ctx context.Context, channelID int64, update IGetMessages) error { @@ -241,26 +305,17 @@ func (t *TelegramClient) onDeleteMessages(ctx context.Context, channelID int64, return nil } -func (t *TelegramClient) updateGhost(ctx context.Context, userID int64, user *tg.User) error { +func (t *TelegramClient) updateGhost(ctx context.Context, userID int64, user *tg.User) (*bridgev2.UserInfo, error) { ghost, err := t.main.Bridge.GetGhostByID(ctx, ids.MakeUserID(userID)) if err != nil { - return err + return nil, err } userInfo, err := t.getUserInfoFromTelegramUser(ctx, user) if err != nil { - return err + return nil, err } ghost.UpdateInfo(ctx, userInfo) - return nil -} - -func (t *TelegramClient) updateGhostWithUserInfo(ctx context.Context, userID int64, userInfo *bridgev2.UserInfo) error { - ghost, err := t.main.Bridge.GetGhostByID(ctx, ids.MakeUserID(userID)) - if err != nil { - return err - } - ghost.UpdateInfo(ctx, userInfo) - return nil + return userInfo, t.maybeUpdateRemoteProfile(ctx, ghost, user) } func (t *TelegramClient) onEntityUpdate(ctx context.Context, e tg.Entities) error {