From 25a99c4464bd1b10732b82a3ecbc12c775bc2605 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Thu, 30 Jul 2020 18:08:26 +0300 Subject: [PATCH] Bridge own read receipts from WhatsApp mobile with double puppeting --- custompuppet.go | 6 +++--- go.mod | 2 +- go.sum | 2 ++ metrics.go | 2 +- user.go | 45 +++++++++++++++++++++++++++++++++++++++++---- 5 files changed, 48 insertions(+), 9 deletions(-) diff --git a/custompuppet.go b/custompuppet.go index c0e46f7..58d2dab 100644 --- a/custompuppet.go +++ b/custompuppet.go @@ -218,7 +218,7 @@ func (puppet *Puppet) handleReceiptEvent(portal *Portal, event *event.Event) { if message == nil { continue } - puppet.customUser.log.Infofln("Marking %s/%s in %s/%s as read", message.JID, message.MXID, portal.Key.JID, portal.MXID) + puppet.customUser.log.Debugfln("Marking %s/%s in %s/%s as read", message.JID, message.MXID, portal.Key.JID, portal.MXID) _, err := puppet.customUser.Conn.Read(portal.Key.JID, message.JID) if err != nil { puppet.customUser.log.Warnln("Error marking read:", err) @@ -238,10 +238,10 @@ func (puppet *Puppet) handleTypingEvent(portal *Portal, evt *event.Event) { puppet.customTypingIn[evt.RoomID] = isTyping presence := whatsapp.PresenceComposing if !isTyping { - puppet.customUser.log.Infofln("Marking not typing in %s/%s", portal.Key.JID, portal.MXID) + puppet.customUser.log.Debugfln("Marking not typing in %s/%s", portal.Key.JID, portal.MXID) presence = whatsapp.PresencePaused } else { - puppet.customUser.log.Infofln("Marking typing in %s/%s", portal.Key.JID, portal.MXID) + puppet.customUser.log.Debugfln("Marking typing in %s/%s", portal.Key.JID, portal.MXID) } _, err := puppet.customUser.Conn.Presence(portal.Key.JID, presence) if err != nil { diff --git a/go.mod b/go.mod index 23e6672..0704814 100644 --- a/go.mod +++ b/go.mod @@ -19,4 +19,4 @@ require ( maunium.net/go/mautrix v0.6.1 ) -replace github.com/Rhymen/go-whatsapp => github.com/tulir/go-whatsapp v0.3.4 +replace github.com/Rhymen/go-whatsapp => github.com/tulir/go-whatsapp v0.3.5 diff --git a/go.sum b/go.sum index 474ee6e..993b61c 100644 --- a/go.sum +++ b/go.sum @@ -117,6 +117,8 @@ github.com/tulir/go-whatsapp v0.3.3 h1:R/SRdgjG1rdmegxx1CE2KmVBKzI8xvC9EE+NNApb4 github.com/tulir/go-whatsapp v0.3.3/go.mod h1:7yGOBdWidM6gsmbAFwgkwHEIhzVrm01+6UbImpMWfTM= github.com/tulir/go-whatsapp v0.3.4 h1:MnfKMj8QOZpZ0SBRXOzhTmoMRF+KqsSDLI+R/glw3rs= github.com/tulir/go-whatsapp v0.3.4/go.mod h1:7yGOBdWidM6gsmbAFwgkwHEIhzVrm01+6UbImpMWfTM= +github.com/tulir/go-whatsapp v0.3.5 h1:cFw8MWhoLTqR0h2kSkSvz866rggRIAx4X2l8I65gARk= +github.com/tulir/go-whatsapp v0.3.5/go.mod h1:7yGOBdWidM6gsmbAFwgkwHEIhzVrm01+6UbImpMWfTM= 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= golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9 h1:vEg9joUBmeBcK9iSJftGNf3coIG4HqZElCPehJsfAYM= diff --git a/metrics.go b/metrics.go index 35a566b..385de7a 100644 --- a/metrics.go +++ b/metrics.go @@ -189,7 +189,7 @@ func (mh *MetricsHandler) Start() { go mh.startUpdatingStats() err := mh.server.ListenAndServe() mh.running = false - if err != nil { + if err != nil && err != http.ErrServerClosed { mh.log.Fatalln("Error in metrics listener:", err) } } diff --git a/user.go b/user.go index ed3be3b..dcea063 100644 --- a/user.go +++ b/user.go @@ -31,6 +31,7 @@ import ( "maunium.net/go/mautrix" "github.com/Rhymen/go-whatsapp" + waBinary "github.com/Rhymen/go-whatsapp/binary" waProto "github.com/Rhymen/go-whatsapp/binary/proto" "maunium.net/go/mautrix/event" @@ -251,8 +252,13 @@ func (user *User) RestoreSession() bool { return true } else if err != nil { user.log.Errorln("Failed to restore session:", err) - user.sendMarkdownBridgeAlert("\u26a0 Failed to connect to WhatsApp. Make sure WhatsApp " + - "on your phone is reachable and use `reconnect` to try connecting again.") + if errors.Is(err, whatsapp.ErrUnpaired) { + user.sendMarkdownBridgeAlert("\u26a0 Failed to connect to WhatsApp: unpaired from phone. " + + "To re-pair your phone, use `delete-session` and then `login`.") + } else { + user.sendMarkdownBridgeAlert("\u26a0 Failed to connect to WhatsApp. Make sure WhatsApp " + + "on your phone is reachable and use `reconnect` to try connecting again.") + } user.log.Debugln("Disconnecting due to failed session restore...") _, err := user.Conn.Disconnect() if err != nil { @@ -850,8 +856,8 @@ func (user *User) HandleMsgInfo(info whatsappExt.MsgInfo) { go func() { intent := user.bridge.GetPuppetByJID(info.SenderJID).IntentFor(portal) - for _, id := range info.IDs { - msg := user.bridge.DB.Message.GetByJID(portal.Key, id) + for _, msgID := range info.IDs { + msg := user.bridge.DB.Message.GetByJID(portal.Key, msgID) if msg == nil { continue } @@ -865,6 +871,33 @@ func (user *User) HandleMsgInfo(info whatsappExt.MsgInfo) { } } +func (user *User) HandleReadMessage(read whatsapp.ReadMessage) { + if strings.HasSuffix(read.Jid, whatsappExt.OldUserSuffix) { + read.Jid = strings.Replace(read.Jid, whatsappExt.OldUserSuffix, whatsappExt.NewUserSuffix, -1) + } + puppet := user.bridge.GetPuppetByJID(user.JID) + if puppet == nil { + return + } + intent := puppet.CustomIntent() + if intent == nil { + return + } + portal := user.GetPortalByJID(read.Jid) + if portal == nil { + return + } + message := user.bridge.DB.Message.GetLastInChat(portal.Key) + if message == nil { + return + } + user.log.Debugfln("User read %s/%s in %s/%s in WhatsApp mobile", message.JID, message.MXID, portal.Key.JID, portal.MXID) + err := intent.MarkRead(portal.MXID, message.MXID) + if err != nil { + user.log.Warnfln("Failed to bridge own read receipt in %s: %v", read.Jid, err) + } +} + func (user *User) HandleCommand(cmd whatsappExt.Command) { switch cmd.Type { case whatsappExt.CommandPicture: @@ -946,6 +979,10 @@ func (user *User) HandleRawMessage(message *waProto.WebMessageInfo) { user.updateLastConnectionIfNecessary() } +func (user *User) HandleUnknownBinaryNode(node *waBinary.Node) { + user.log.Debugfln("Unknown binary message: %+v", node) +} + func (user *User) NeedsRelaybot(portal *Portal) bool { return !user.HasSession() || !user.IsInPortal(portal.Key) }