From e0194f7621de1e1f3ac987d6d630f75ab56bef3c Mon Sep 17 00:00:00 2001 From: Sumner Evans Date: Tue, 6 Aug 2024 15:02:39 -0600 Subject: [PATCH] typing: support TG <-> Matrix Signed-off-by: Sumner Evans --- go.mod | 2 +- go.sum | 4 ++-- pkg/connector/client.go | 27 +++++++++++++++++++++++-- pkg/connector/directdownload.go | 3 ++- pkg/connector/ids/ids.go | 22 ++++++++++++++++----- pkg/connector/matrix.go | 13 +++++++++--- pkg/connector/telegram.go | 35 ++++++++++++++++++++++++++++----- 7 files changed, 87 insertions(+), 19 deletions(-) diff --git a/go.mod b/go.mod index ad3f55ab..89675849 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( go.uber.org/zap v1.27.0 golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 golang.org/x/net v0.27.0 - maunium.net/go/mautrix v0.19.1-0.20240806185340-213f9df4a467 + maunium.net/go/mautrix v0.19.1-0.20240807155838-eabab275895d ) require ( diff --git a/go.sum b/go.sum index 887477b3..d1897ffa 100644 --- a/go.sum +++ b/go.sum @@ -112,8 +112,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.19.1-0.20240806185340-213f9df4a467 h1:QptF4mA070qVG2isInl2HjtPOZ1TTqf0zM38uHPeWM8= -maunium.net/go/mautrix v0.19.1-0.20240806185340-213f9df4a467/go.mod h1:ZWyxoQxRTBxzWIMs0kQCVogZIY0clTu33h102veCT/Q= +maunium.net/go/mautrix v0.19.1-0.20240807155838-eabab275895d h1:+PtYgqxswmN5UM9XSLKO88TjYJHApwWn0j4fAnCslxg= +maunium.net/go/mautrix v0.19.1-0.20240807155838-eabab275895d/go.mod h1:ZWyxoQxRTBxzWIMs0kQCVogZIY0clTu33h102veCT/Q= 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/client.go b/pkg/connector/client.go index d02f4b11..ad60bb83 100644 --- a/pkg/connector/client.go +++ b/pkg/connector/client.go @@ -129,6 +129,19 @@ func NewTelegramClient(ctx context.Context, tc *TelegramConnector, login *bridge dispatcher.OnEditChannelMessage(func(ctx context.Context, e tg.Entities, update *tg.UpdateEditChannelMessage) error { return client.onMessageEdit(ctx, update) }) + dispatcher.OnUserTyping(func(ctx context.Context, e tg.Entities, update *tg.UpdateUserTyping) error { + return client.handleTyping(ids.PeerTypeUser.AsPortalKey(update.UserID, login.ID), update.UserID, update.Action) + }) + dispatcher.OnChatUserTyping(func(ctx context.Context, e tg.Entities, update *tg.UpdateChatUserTyping) error { + if update.FromID.TypeID() != tg.PeerUserTypeID { + log.Warn().Str("from_id_type", update.FromID.TypeName()).Msg("unsupported from_id type") + return nil + } + return client.handleTyping(ids.PeerTypeChat.AsPortalKey(update.ChatID, login.ID), update.FromID.(*tg.PeerUser).UserID, update.Action) + }) + dispatcher.OnChannelUserTyping(func(ctx context.Context, e tg.Entities, update *tg.UpdateChannelUserTyping) error { + return client.handleTyping(ids.PeerTypeChannel.AsPortalKey(update.ChannelID, ""), update.FromID.(*tg.PeerUser).UserID, update.Action) + }) client.ScopedStore = tc.Store.GetScopedStore(telegramUserID) @@ -202,9 +215,19 @@ func NewTelegramClient(ctx context.Context, tc *TelegramConnector, login *bridge var portalKey networkid.PortalKey if strings.HasPrefix(group, "C/") || strings.HasPrefix(group, "c/") { - portalKey = networkid.PortalKey{ID: networkid.PortalID(fmt.Sprintf("%s:%s", ids.PeerTypeChannel, group[2:]))} + chatID, err := strconv.ParseInt(submatches[1][2:], 10, 64) + if err != nil { + log.Err(err).Msg("error parsing channel ID") + return url + } + portalKey = ids.PeerTypeChannel.AsPortalKey(chatID, "") } else { - portalKey = networkid.PortalKey{ID: networkid.PortalID(fmt.Sprintf("%s:%s", ids.PeerTypeUser, group))} + userID, err := strconv.ParseInt(submatches[1], 10, 64) + if err != nil { + log.Err(err).Msg("error parsing user ID") + return url + } + portalKey = ids.PeerTypeUser.AsPortalKey(userID, login.ID) } portal, err := tc.Bridge.DB.Portal.GetByKey(ctx, portalKey) diff --git a/pkg/connector/directdownload.go b/pkg/connector/directdownload.go index ceaac3ee..0872d045 100644 --- a/pkg/connector/directdownload.go +++ b/pkg/connector/directdownload.go @@ -34,7 +34,8 @@ func (tc *TelegramConnector) Download(ctx context.Context, mediaID networkid.Med ctx = log.WithContext(ctx) log.Info().Msg("handling direct download") - logins, err := tc.Bridge.GetUserLoginsInPortal(ctx, info.PeerType.AsPortalKey(info.ChatID)) + // TODO fix this + logins, err := tc.Bridge.GetUserLoginsInPortal(ctx, info.PeerType.AsPortalKey(info.ChatID, "")) if err != nil { return nil, err } else if len(logins) == 0 { diff --git a/pkg/connector/ids/ids.go b/pkg/connector/ids/ids.go index 3386fb44..ea84e6a8 100644 --- a/pkg/connector/ids/ids.go +++ b/pkg/connector/ids/ids.go @@ -68,16 +68,28 @@ func (pt PeerType) AsByte() byte { } } -func (pt PeerType) AsPortalKey(chatID int64) networkid.PortalKey { - return networkid.PortalKey{ID: networkid.PortalID(fmt.Sprintf("%s:%d", pt, chatID))} +func (pt PeerType) AsPortalKey(chatID int64, receiver networkid.UserLoginID) networkid.PortalKey { + portalKey := networkid.PortalKey{ + ID: networkid.PortalID(fmt.Sprintf("%s:%d", pt, chatID)), + } + if pt == PeerTypeUser || pt == PeerTypeChat { + portalKey.Receiver = receiver + } + return portalKey } -func MakePortalKey(peer tg.PeerClass) networkid.PortalKey { +func MakePortalKey(peer tg.PeerClass, receiver networkid.UserLoginID) networkid.PortalKey { switch v := peer.(type) { case *tg.PeerUser: - return networkid.PortalKey{ID: networkid.PortalID(fmt.Sprintf("%s:%d", PeerTypeUser, v.UserID))} + return networkid.PortalKey{ + ID: networkid.PortalID(fmt.Sprintf("%s:%d", PeerTypeUser, v.UserID)), + Receiver: receiver, + } case *tg.PeerChat: - return networkid.PortalKey{ID: networkid.PortalID(fmt.Sprintf("%s:%d", PeerTypeChat, v.ChatID))} + return networkid.PortalKey{ + ID: networkid.PortalID(fmt.Sprintf("%s:%d", PeerTypeChat, v.ChatID)), + Receiver: receiver, + } case *tg.PeerChannel: return networkid.PortalKey{ID: networkid.PortalID(fmt.Sprintf("%s:%d", PeerTypeChannel, v.ChannelID))} default: diff --git a/pkg/connector/matrix.go b/pkg/connector/matrix.go index 4d6a7f90..18a9e9bd 100644 --- a/pkg/connector/matrix.go +++ b/pkg/connector/matrix.go @@ -182,7 +182,7 @@ func (t *TelegramClient) HandleMatrixMessage(ctx context.Context, msg *bridgev2. DB: &database.Message{ ID: ids.MakeMessageID(tgMessageID), MXID: msg.Event.ID, - Room: networkid.PortalKey{ID: msg.Portal.ID}, + Room: msg.Portal.PortalKey, SenderID: t.userID, Timestamp: time.Unix(int64(tgDate), 0), Metadata: &MessageMetadata{ @@ -401,6 +401,13 @@ func (t *TelegramClient) HandleMatrixReadReceipt(ctx context.Context, msg *bridg } func (t *TelegramClient) HandleMatrixTyping(ctx context.Context, msg *bridgev2.MatrixTyping) error { - // TODO - return nil + inputPeer, err := t.inputPeerForPortalID(ctx, msg.Portal.ID) + if err != nil { + return err + } + _, err = t.client.API().MessagesSetTyping(ctx, &tg.MessagesSetTypingRequest{ + Peer: inputPeer, + Action: &tg.SendMessageTypingAction{}, + }) + return err } diff --git a/pkg/connector/telegram.go b/pkg/connector/telegram.go index 9d8bfdb1..966095e7 100644 --- a/pkg/connector/telegram.go +++ b/pkg/connector/telegram.go @@ -55,7 +55,7 @@ func (t *TelegramClient) onUpdateNewMessage(ctx context.Context, update IGetMess Stringer("peer_id", msg.PeerID) }, Sender: sender, - PortalKey: ids.MakePortalKey(msg.PeerID), + PortalKey: ids.MakePortalKey(msg.PeerID, t.loginID), CreatePortal: true, Timestamp: time.Unix(int64(msg.Date), 0), }, @@ -69,7 +69,7 @@ func (t *TelegramClient) onUpdateNewMessage(ctx context.Context, update IGetMess chatInfoChange := simplevent.ChatInfoChange{ EventMeta: simplevent.EventMeta{ Type: bridgev2.RemoteEventChatInfoChange, - PortalKey: ids.MakePortalKey(msg.PeerID), + PortalKey: ids.MakePortalKey(msg.PeerID, t.loginID), Sender: sender, Timestamp: time.Unix(int64(msg.Date), 0), LogContext: func(c zerolog.Context) zerolog.Context { @@ -279,7 +279,7 @@ func (t *TelegramClient) onMessageEdit(ctx context.Context, update IGetMessage) Int("message_id", msg.ID) }, Sender: sender, - PortalKey: ids.MakePortalKey(msg.PeerID), + PortalKey: ids.MakePortalKey(msg.PeerID, t.loginID), Timestamp: time.Unix(int64(msg.EditDate), 0), }, ID: ids.MakeMessageID(msg.ID), @@ -305,6 +305,27 @@ func (t *TelegramClient) onMessageEdit(ctx context.Context, update IGetMessage) return nil } +func (t *TelegramClient) handleTyping(portal networkid.PortalKey, userID int64, action tg.SendMessageActionClass) error { + if userID == t.telegramUserID { + return nil + } + timeout := time.Duration(6) * time.Second + if action.TypeID() != tg.SendMessageTypingActionTypeID { + timeout = 0 + } + t.main.Bridge.QueueRemoteEvent(t.userLogin, &simplevent.Typing{ + EventMeta: simplevent.EventMeta{ + Type: bridgev2.RemoteEventTyping, + PortalKey: portal, + Sender: bridgev2.EventSender{ + SenderLogin: ids.MakeUserLoginID(userID), + Sender: ids.MakeUserID(userID), + }, + }, + Timeout: timeout, + }) + return nil +} func (t *TelegramClient) handleTelegramReactions(ctx context.Context, msg *tg.Message) { log := zerolog.Ctx(ctx).With(). @@ -353,7 +374,7 @@ func (t *TelegramClient) handleTelegramReactions(ctx context.Context, msg *tg.Me // return // TODO should calls to this be limited? - } else if peer, err := t.inputPeerForPortalID(ctx, ids.MakePortalKey(msg.PeerID).ID); err != nil { + } else if peer, err := t.inputPeerForPortalID(ctx, ids.MakePortalKey(msg.PeerID, t.loginID).ID); err != nil { log.Err(err).Msg("failed to get input peer") return } else { @@ -447,7 +468,11 @@ func (t *TelegramClient) inputPeerForPortalID(ctx context.Context, portalID netw } switch peerType { case ids.PeerTypeUser: - return &tg.InputPeerUser{UserID: id}, nil + if ghost, err := t.main.Bridge.DB.Ghost.GetByID(ctx, ids.MakeUserID(id)); err != nil { + return nil, err + } else { + return &tg.InputPeerUser{UserID: id, AccessHash: ghost.Metadata.(*GhostMetadata).AccessHash}, nil + } case ids.PeerTypeChat: return &tg.InputPeerChat{ChatID: id}, nil case ids.PeerTypeChannel: