From e2817b9e9bb2f0583b8f79f8606a0a7b049a1e28 Mon Sep 17 00:00:00 2001 From: highesttt Date: Sat, 20 Dec 2025 00:55:15 -0600 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20Added=20read=20recei?= =?UTF-8?q?pts?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pkg/connector/client.go | 46 +++++++++++++++++++++++++++++++++++++++++ pkg/line/methods.go | 5 +++++ 2 files changed, 51 insertions(+) diff --git a/pkg/connector/client.go b/pkg/connector/client.go index 4354ba8..df83af4 100644 --- a/pkg/connector/client.go +++ b/pkg/connector/client.go @@ -41,6 +41,7 @@ type LineClient struct { } var _ bridgev2.NetworkAPI = (*LineClient)(nil) +var _ bridgev2.ReadReceiptHandlingNetworkAPI = (*LineClient)(nil) func (lc *LineClient) Connect(ctx context.Context) { if lc.peerKeys == nil { @@ -253,6 +254,26 @@ func (lc *LineClient) handleOperation(_ context.Context, op line.Operation) { // Type 25 = SEND_MESSAGE (Message sent by you from another device) // Type 26 = RECEIVE_MESSAGE (Message received from another user) + if op.Type == 55 { + senderID := makeUserID(op.Param1) + portalID := makePortalID(op.Param2) + // Param 2 is the group id or sender id in 1:1 chats + + ts, _ := op.CreatedTime.Int64() + lc.UserLogin.Bridge.QueueRemoteEvent(lc.UserLogin, &simplevent.Receipt{ + EventMeta: simplevent.EventMeta{ + Type: bridgev2.RemoteEventReadReceipt, + PortalKey: networkid.PortalKey{ + ID: portalID, + Receiver: lc.UserLogin.ID, + }, + Timestamp: time.UnixMilli(ts), + Sender: bridgev2.EventSender{Sender: senderID}, + }, + ReadUpTo: time.UnixMilli(ts), + }) + } + if op.Type == 25 { lc.reqSeqMu.Lock() _, ok := lc.sentReqSeqs[op.ReqSeq] @@ -339,6 +360,31 @@ func (lc *LineClient) queueIncomingMessage(msg *line.Message, opType int) { func (lc *LineClient) Disconnect() {} +func (lc *LineClient) HandleMatrixReadReceipt(ctx context.Context, read *bridgev2.MatrixReadReceipt) error { + if read.ReadUpTo.IsZero() && read.EventID == "" { + return nil + } + + targetID := string(read.EventID) + if read.EventID != "" { + msg, err := lc.UserLogin.Bridge.DB.Message.GetPartByMXID(ctx, read.EventID) + if err == nil && msg != nil && msg.ID != "" { + targetID = string(msg.ID) + } + } + + if targetID == "" || strings.HasPrefix(targetID, "$") { + return nil + } + + client := line.NewClient(lc.AccessToken) + err := client.SendChatChecked(string(read.Portal.ID), targetID) + if err != nil { + return err + } + return nil +} + func (lc *LineClient) IsLoggedIn() bool { return lc.AccessToken != "" } func (lc *LineClient) LogoutRemote(ctx context.Context) {} diff --git a/pkg/line/methods.go b/pkg/line/methods.go index 8cd4a6b..e883524 100644 --- a/pkg/line/methods.go +++ b/pkg/line/methods.go @@ -265,6 +265,11 @@ func (c *Client) SendMessage(reqSeq int64, msg *Message) error { return nil } +func (c *Client) SendChatChecked(chatMid, messageID string) error { + _, err := c.callRPC("TalkService", "sendChatChecked", 0, chatMid, messageID) + return err +} + // GetContactsV2 fetches contact details for a list of MIDs. func (c *Client) GetContactsV2(mids []string) (*ContactsResponse, error) { req := GetContactsV2Request{TargetUserMids: mids} From 8b62f2d3cf1083ec2a37b56cce186abeda13ebb1 Mon Sep 17 00:00:00 2001 From: highesttt Date: Sat, 20 Dec 2025 01:03:02 -0600 Subject: [PATCH 2/2] =?UTF-8?q?fix:=20=F0=9F=90=9B=20Missing=20method=20co?= =?UTF-8?q?mment=20&=20redundant=20error=20check?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ✅ Closes: #1 --- pkg/connector/client.go | 6 +----- pkg/line/methods.go | 1 + 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/pkg/connector/client.go b/pkg/connector/client.go index df83af4..6e501c4 100644 --- a/pkg/connector/client.go +++ b/pkg/connector/client.go @@ -378,11 +378,7 @@ func (lc *LineClient) HandleMatrixReadReceipt(ctx context.Context, read *bridgev } client := line.NewClient(lc.AccessToken) - err := client.SendChatChecked(string(read.Portal.ID), targetID) - if err != nil { - return err - } - return nil + return client.SendChatChecked(string(read.Portal.ID), targetID) } func (lc *LineClient) IsLoggedIn() bool { return lc.AccessToken != "" } diff --git a/pkg/line/methods.go b/pkg/line/methods.go index e883524..a0fa5b2 100644 --- a/pkg/line/methods.go +++ b/pkg/line/methods.go @@ -265,6 +265,7 @@ func (c *Client) SendMessage(reqSeq int64, msg *Message) error { return nil } +// SendChatChecked sends a read receipt for a message in a chat func (c *Client) SendChatChecked(chatMid, messageID string) error { _, err := c.callRPC("TalkService", "sendChatChecked", 0, chatMid, messageID) return err