Fix bridging receipts with multiple message IDs

This commit is contained in:
Tulir Asokan 2021-11-10 20:26:28 +02:00
parent 5c1b57d7b8
commit 6b260dceda
3 changed files with 27 additions and 53 deletions

2
go.mod
View file

@ -8,7 +8,7 @@ require (
github.com/mattn/go-sqlite3 v1.14.9
github.com/prometheus/client_golang v1.11.0
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
go.mau.fi/whatsmeow v0.0.0-20211109215855-2e8804d7e690
go.mau.fi/whatsmeow v0.0.0-20211110182414-ba28bae40fb5
golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d
google.golang.org/protobuf v1.27.1
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b

4
go.sum
View file

@ -139,8 +139,8 @@ github.com/tidwall/sjson v1.2.3 h1:5+deguEhHSEjmuICXZ21uSSsXotWMA0orU783+Z7Cp8=
github.com/tidwall/sjson v1.2.3/go.mod h1:5WdjKx3AQMvCJ4RG6/2UYT7dLrGvJUV1x4jdTAyGvZs=
go.mau.fi/libsignal v0.0.0-20211109153248-a67163214910 h1:9FFhG0OmkuMau5UEaTgiUQ+7cSbtbOQ7hiWKdN8OI3I=
go.mau.fi/libsignal v0.0.0-20211109153248-a67163214910/go.mod h1:AufGrvVh+00Nc07Jm4hTquh7yleZyn20tKJI2wCPAKg=
go.mau.fi/whatsmeow v0.0.0-20211109215855-2e8804d7e690 h1:Yx5v2ut+kd5YG+bLhCxqPnCEZ+yau2wKsQerk3VYtbY=
go.mau.fi/whatsmeow v0.0.0-20211109215855-2e8804d7e690/go.mod h1:8jUjOAi3xtGubxcZgG8uSHpAdyQXBRbWAfxkctX/4y4=
go.mau.fi/whatsmeow v0.0.0-20211110182414-ba28bae40fb5 h1:ERlXDWd0vj62SldgFSqCkjtVpyUgmxRtyKMH20ADW7Q=
go.mau.fi/whatsmeow v0.0.0-20211110182414-ba28bae40fb5/go.mod h1:8jUjOAi3xtGubxcZgG8uSHpAdyQXBRbWAfxkctX/4y4=
golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=

74
user.go
View file

@ -722,58 +722,32 @@ func (user *User) handleReceipt(receipt *events.Receipt) {
if portal == nil || len(portal.MXID) == 0 {
return
}
if receipt.IsFromMe {
user.markSelfRead(portal, receipt.MessageID)
} else {
intent := user.bridge.GetPuppetByJID(receipt.Sender).IntentFor(portal)
ok := user.markOtherRead(portal, intent, receipt.MessageID)
if !ok {
// Message not found, try any previous IDs
for i := len(receipt.PreviousIDs) - 1; i >= 0; i-- {
ok = user.markOtherRead(portal, intent, receipt.PreviousIDs[i])
if ok {
break
}
}
// The order of the message ID array depends on the sender's platform, so we just have to find
// the last message based on timestamp. Also, timestamps only have second precision, so if
// there are many messages at the same second just mark them all as read, because we don't
// know which one is last
markAsRead := make([]*database.Message, 0, 1)
var bestTimestamp time.Time
for _, msgID := range receipt.MessageIDs {
msg := user.bridge.DB.Message.GetByJID(portal.Key, msgID)
if msg == nil || msg.IsFakeMXID() {
continue
}
if msg.Timestamp.After(bestTimestamp) {
bestTimestamp = msg.Timestamp
markAsRead = append(markAsRead[:0], msg)
} else if msg != nil && msg.Timestamp.Equal(bestTimestamp) {
markAsRead = append(markAsRead, msg)
}
}
}
func (user *User) markOtherRead(portal *Portal, intent *appservice.IntentAPI, messageID types.MessageID) bool {
msg := user.bridge.DB.Message.GetByJID(portal.Key, messageID)
if msg == nil || msg.IsFakeMXID() {
return false
}
err := intent.MarkReadWithContent(portal.MXID, msg.MXID, &CustomReadReceipt{DoublePuppet: intent.IsCustomPuppet})
if err != nil {
user.log.Warnfln("Failed to mark message %s as read by %s: %v", msg.MXID, intent.UserID, err)
}
return true
}
func (user *User) markSelfRead(portal *Portal, messageID types.MessageID) {
puppet := user.bridge.GetPuppetByCustomMXID(user.MXID)
if puppet == nil || puppet.CustomIntent() == nil {
return
}
var message *database.Message
if messageID == "" {
message = user.bridge.DB.Message.GetLastInChat(portal.Key)
if message == nil {
return
intent := user.bridge.GetPuppetByJID(receipt.Sender).IntentFor(portal)
for _, msg := range markAsRead {
err := intent.MarkReadWithContent(portal.MXID, msg.MXID, &CustomReadReceipt{DoublePuppet: intent.IsCustomPuppet})
if err != nil {
user.log.Warnfln("Failed to mark message %s as read by %s: %v", msg.MXID, intent.UserID, err)
} else {
user.log.Debugfln("Marked %s as read by %s", msg.MXID, intent.UserID)
}
user.log.Debugfln("User read chat %s/%s in WhatsApp mobile (last known event: %s/%s)", portal.Key.JID, portal.MXID, message.JID, message.MXID)
} else {
message = user.bridge.DB.Message.GetByJID(portal.Key, messageID)
if message == nil || message.IsFakeMXID() {
return
}
user.log.Debugfln("User read message %s/%s in %s/%s in WhatsApp mobile", message.JID, message.MXID, portal.Key.JID, portal.MXID)
}
err := puppet.CustomIntent().MarkReadWithContent(portal.MXID, message.MXID, &CustomReadReceipt{DoublePuppet: true})
if err != nil {
user.log.Warnfln("Failed to bridge own read receipt in %s: %v", portal.Key.JID, err)
}
}
@ -788,7 +762,7 @@ func (user *User) markSelfReadFull(portal *Portal) {
}
err := puppet.CustomIntent().MarkReadWithContent(portal.MXID, lastMessage.MXID, &CustomReadReceipt{DoublePuppet: true})
if err != nil {
user.log.Warnfln("Failed to mark %s in %s as read after backfill: %v", lastMessage.MXID, portal.MXID, err)
user.log.Warnfln("Failed to mark %s (last message) in %s as read: %v", lastMessage.MXID, portal.MXID, err)
}
}