From 835afb01006440632cba98c2d17a4607809646c2 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Thu, 2 Apr 2026 23:56:08 +0300 Subject: [PATCH] matrixfmt,telegramfmt: correctly bridge mentions of other logged-in users --- pkg/connector/client.go | 21 +++++++++++++++++---- pkg/connector/handlematrix.go | 4 ++-- pkg/connector/ids/ids.go | 4 ++++ pkg/connector/matrixfmt/convert.go | 5 +++-- pkg/connector/matrixfmt/html.go | 10 ++++++---- 5 files changed, 32 insertions(+), 12 deletions(-) diff --git a/pkg/connector/client.go b/pkg/connector/client.go index 8b06803c..6f1919cc 100644 --- a/pkg/connector/client.go +++ b/pkg/connector/client.go @@ -236,6 +236,8 @@ func NewTelegramClient(ctx context.Context, tc *TelegramConnector, login *bridge // FIXME this should look for user logins by ID, not hardcode the current user if id == client.telegramUserID { userInfo.MXID = client.userLogin.UserMXID + } else if login := tc.Bridge.GetCachedUserLoginByID(ids.MakeUserLoginID(id)); login != nil { + userInfo.MXID = login.UserMXID } return userInfo, nil }, @@ -250,6 +252,8 @@ func NewTelegramClient(ctx context.Context, tc *TelegramConnector, login *bridge userInfo := telegramfmt.UserInfo{MXID: ghost.Intent.GetMXID(), Name: ghost.Name} if ghost.ID == client.userID { userInfo.MXID = client.userLogin.UserMXID + } else if login := tc.Bridge.GetCachedUserLoginByID(ids.MakeUserLoginID(userID)); login != nil { + userInfo.MXID = login.UserMXID } return userInfo, nil } @@ -334,10 +338,19 @@ func NewTelegramClient(ctx context.Context, tc *TelegramConnector, login *bridge } client.matrixParser = &matrixfmt.HTMLParser{ Store: tc.Store, - GetGhostDetails: func(ctx context.Context, ui id.UserID) (networkid.UserID, string, int64, bool) { - if userID, ok := tc.Bridge.Matrix.ParseGhostMXID(ui); !ok { - return "", "", 0, false - } else if peerType, telegramUserID, err := ids.ParseUserID(userID); err != nil { + GetGhostDetails: func(ctx context.Context, portal *bridgev2.Portal, ui id.UserID) (networkid.UserID, string, int64, bool) { + userID, ok := tc.Bridge.Matrix.ParseGhostMXID(ui) + if !ok { + user, err := tc.Bridge.GetExistingUserByMXID(ctx, ui) + if err != nil { + return "", "", 0, false + } else if login, _, _ := portal.FindPreferredLogin(ctx, user, false); login != nil { + userID = ids.UserLoginIDToUserID(login.ID) + } else { + return "", "", 0, false + } + } + if peerType, telegramUserID, err := ids.ParseUserID(userID); err != nil { return "", "", 0, false } else if accessHash, err := client.ScopedStore.GetAccessHash(ctx, peerType, telegramUserID); err != nil || accessHash == 0 { return "", "", 0, false diff --git a/pkg/connector/handlematrix.go b/pkg/connector/handlematrix.go index 84520b7b..2b176a5e 100644 --- a/pkg/connector/handlematrix.go +++ b/pkg/connector/handlematrix.go @@ -418,7 +418,7 @@ func (t *TelegramClient) HandleMatrixMessage(ctx context.Context, msg *bridgev2. noWebpage := msg.Content.BeeperLinkPreviews != nil && len(msg.Content.BeeperLinkPreviews) == 0 - message, entities := matrixfmt.Parse(ctx, t.matrixParser, msg.Content) + message, entities := matrixfmt.Parse(ctx, t.matrixParser, msg.Content, msg.Portal) var replyTo tg.InputReplyToClass if msg.ReplyTo != nil { @@ -611,7 +611,7 @@ func (t *TelegramClient) HandleMatrixEdit(ctx context.Context, msg *bridgev2.Mat return err } - message, entities := matrixfmt.Parse(ctx, t.matrixParser, msg.Content) + message, entities := matrixfmt.Parse(ctx, t.matrixParser, msg.Content, msg.Portal) var newContentURI id.ContentURIString req := tg.MessagesEditMessageRequest{ diff --git a/pkg/connector/ids/ids.go b/pkg/connector/ids/ids.go index 3e5424e2..8cfbc36e 100644 --- a/pkg/connector/ids/ids.go +++ b/pkg/connector/ids/ids.go @@ -56,6 +56,10 @@ func ParseUserLoginID(userID networkid.UserLoginID) (int64, error) { return strconv.ParseInt(string(userID), 10, 64) } +func UserLoginIDToUserID(userLoginID networkid.UserLoginID) networkid.UserID { + return networkid.UserID(userLoginID) +} + func MakeUserLoginID(userID int64) networkid.UserLoginID { if userID == 0 { return "" diff --git a/pkg/connector/matrixfmt/convert.go b/pkg/connector/matrixfmt/convert.go index e1340a36..7c888cf3 100644 --- a/pkg/connector/matrixfmt/convert.go +++ b/pkg/connector/matrixfmt/convert.go @@ -20,6 +20,7 @@ import ( "context" "fmt" + "maunium.net/go/mautrix/bridgev2" "maunium.net/go/mautrix/event" "go.mau.fi/mautrix-telegram/pkg/connector/ids" @@ -87,7 +88,7 @@ func toTelegramEntity(br telegramfmt.BodyRange) tg.MessageEntityClass { } } -func Parse(ctx context.Context, parser *HTMLParser, content *event.MessageEventContent) (string, []tg.MessageEntityClass) { +func Parse(ctx context.Context, parser *HTMLParser, content *event.MessageEventContent, portal *bridgev2.Portal) (string, []tg.MessageEntityClass) { if content.MsgType.IsMedia() && (content.FileName == "" || content.FileName == content.Body) { // The body is the filename. return "", nil @@ -96,7 +97,7 @@ func Parse(ctx context.Context, parser *HTMLParser, content *event.MessageEventC if content.Format != event.FormatHTML { return content.Body, nil } - parseCtx := NewContext(ctx) + parseCtx := NewContext(ctx, portal) parseCtx.AllowedMentions = content.Mentions parsed := parser.Parse(content.FormattedBody, parseCtx) if parsed == nil { diff --git a/pkg/connector/matrixfmt/html.go b/pkg/connector/matrixfmt/html.go index 43d511e3..2a19e7b5 100644 --- a/pkg/connector/matrixfmt/html.go +++ b/pkg/connector/matrixfmt/html.go @@ -25,6 +25,7 @@ import ( "strings" "golang.org/x/net/html" + "maunium.net/go/mautrix/bridgev2" "maunium.net/go/mautrix/bridgev2/networkid" "maunium.net/go/mautrix/event" "maunium.net/go/mautrix/id" @@ -221,16 +222,18 @@ func (ts TagStack) Has(tag string) bool { type Context struct { Ctx context.Context + Portal *bridgev2.Portal AllowedMentions *event.Mentions TagStack TagStack PreserveWhitespace bool ListDepth int } -func NewContext(ctx context.Context) Context { +func NewContext(ctx context.Context, portal *bridgev2.Portal) Context { return Context{ Ctx: ctx, TagStack: make(TagStack, 0, 4), + Portal: portal, } } @@ -251,7 +254,7 @@ func (ctx Context) WithIncrementedListDepth() Context { // HTMLParser is a somewhat customizable Matrix HTML parser. type HTMLParser struct { - GetGhostDetails func(context.Context, id.UserID) (networkid.UserID, string, int64, bool) + GetGhostDetails func(context.Context, *bridgev2.Portal, id.UserID) (networkid.UserID, string, int64, bool) Store *store.Container } @@ -381,8 +384,7 @@ func (parser *HTMLParser) linkToString(node *html.Node, ctx Context) *EntityStri // Mention not allowed, use name as-is return str } - // FIXME this or GetGhostDetails needs to support non-ghost users too - userID, username, accessHash, ok := parser.GetGhostDetails(ctx.Ctx, mxid) + userID, username, accessHash, ok := parser.GetGhostDetails(ctx.Ctx, ctx.Portal, mxid) if !ok { return str } else if username == "" {