From a9fd97932bed9f42d97d986d60cd36a532a9668d Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Thu, 30 May 2019 17:00:36 +0300 Subject: [PATCH] Send alerts about incoming calls --- portal.go | 31 +++++++++++----- user.go | 29 +++++++++++++++ whatsapp-ext/call.go | 72 +++++++++++++++++++++++++++++++++++++ whatsapp-ext/jsonmessage.go | 3 ++ 4 files changed, 127 insertions(+), 8 deletions(-) create mode 100644 whatsapp-ext/call.go diff --git a/portal.go b/portal.go index 7da5f1e..d5039da 100644 --- a/portal.go +++ b/portal.go @@ -168,6 +168,9 @@ func (portal *Portal) handleMessageLoop() { } func (portal *Portal) handleMessage(msg PortalMessage) { + if len(portal.MXID) == 0 { + return + } switch data := msg.data.(type) { case whatsapp.TextMessage: portal.HandleTextMessage(msg.source, data) @@ -181,6 +184,8 @@ func (portal *Portal) handleMessage(msg PortalMessage) { portal.HandleMediaMessage(msg.source, data.Download, data.Thumbnail, data.Info, data.Type, data.Title) case whatsappExt.MessageRevocation: portal.HandleMessageRevoke(msg.source, data) + case FakeMessage: + portal.HandleFakeMessage(msg.source, data) } } @@ -764,16 +769,30 @@ func (portal *Portal) HandleMessageRevoke(user *User, message whatsappExt.Messag msg.Delete() } +func (portal *Portal) HandleFakeMessage(source *User, message FakeMessage) { + if portal.isRecentlyHandled(message.ID) { + return + } + + _, err := portal.MainIntent().SendText(portal.MXID, message.Text) + if err != nil { + portal.log.Errorfln("Failed to handle fake message %s: %v", message.ID, err) + return + } + + portal.recentlyHandledLock.Lock() + index := portal.recentlyHandledIndex + portal.recentlyHandledIndex = (portal.recentlyHandledIndex + 1) % recentlyHandledLength + portal.recentlyHandledLock.Unlock() + portal.recentlyHandled[index] = message.ID +} + type MessageContent struct { *mautrix.Content IsCustomPuppet bool `json:"net.maunium.whatsapp.puppet,omitempty"` } func (portal *Portal) HandleTextMessage(source *User, message whatsapp.TextMessage) { - if len(portal.MXID) == 0 { - return - } - if !portal.startHandling(message.Info) { return } @@ -801,10 +820,6 @@ func (portal *Portal) HandleTextMessage(source *User, message whatsapp.TextMessa } func (portal *Portal) HandleMediaMessage(source *User, download func() ([]byte, error), thumbnail []byte, info whatsapp.MessageInfo, mimeType, caption string) { - if len(portal.MXID) == 0 { - return - } - if !portal.startHandling(info) { return } diff --git a/user.go b/user.go index ec85b41..ed2940d 100644 --- a/user.go +++ b/user.go @@ -489,6 +489,35 @@ func (user *User) HandleMessageRevoke(message whatsappExt.MessageRevocation) { user.putMessage(PortalMessage{message.RemoteJid, user, message, 0}) } +type FakeMessage struct { + Text string + ID string +} + +func (user *User) HandleCallInfo(info whatsappExt.CallInfo) { + if info.Data != nil { + return + } + data := FakeMessage{ + ID: info.ID, + } + switch info.Type { + case whatsappExt.CallOffer: + data.Text = "Incoming call" + case whatsappExt.CallOfferVideo: + data.Text = "Incoming video call" + case whatsappExt.CallTerminate: + data.Text = "Call ended" + data.ID += "E" + default: + return + } + portal := user.GetPortalByJID(info.From) + if portal != nil { + portal.messages <- PortalMessage{info.From, user, data, 0} + } +} + func (user *User) HandlePresence(info whatsappExt.Presence) { puppet := user.bridge.GetPuppetByJID(info.SenderJID) switch info.Status { diff --git a/whatsapp-ext/call.go b/whatsapp-ext/call.go new file mode 100644 index 0000000..ce6d361 --- /dev/null +++ b/whatsapp-ext/call.go @@ -0,0 +1,72 @@ +// mautrix-whatsapp - A Matrix-WhatsApp puppeting bridge. +// Copyright (C) 2019 Tulir Asokan +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package whatsappExt + +import ( + "encoding/json" + "strings" + + "github.com/Rhymen/go-whatsapp" +) + +type CallInfoType string + +const ( + CallOffer CallInfoType = "offer" + CallOfferVideo CallInfoType = "offer_video" + CallTransport CallInfoType = "transport" + CallRelayLatency CallInfoType = "relaylatency" + CallTerminate CallInfoType = "terminate" +) + +type CallInfo struct { + ID string `json:"id"` + Type CallInfoType `json:"type"` + From string `json:"from"` + + Platform string `json:"platform"` + Version []int `json:"version"` + + Data [][]interface{} `json:"data"` +} + +type CallInfoHandler interface { + whatsapp.Handler + HandleCallInfo(CallInfo) +} + +func (ext *ExtendedConn) handleMessageCall(message []byte) { + var event CallInfo + err := json.Unmarshal(message, &event) + if err != nil { + ext.jsonParseError(err) + return + } + event.From = strings.Replace(event.From, OldUserSuffix, NewUserSuffix, 1) + for _, handler := range ext.handlers { + callInfoHandler, ok := handler.(CallInfoHandler) + if !ok { + continue + } + + if ext.shouldCallSynchronously(callInfoHandler) { + callInfoHandler.HandleCallInfo(event) + } else { + go callInfoHandler.HandleCallInfo(event) + } + } +} diff --git a/whatsapp-ext/jsonmessage.go b/whatsapp-ext/jsonmessage.go index be6fb15..d7340e1 100644 --- a/whatsapp-ext/jsonmessage.go +++ b/whatsapp-ext/jsonmessage.go @@ -35,6 +35,7 @@ const ( MessageProps JSONMessageType = "Props" MessageCmd JSONMessageType = "Cmd" MessageChat JSONMessageType = "Chat" + MessageCall JSONMessageType = "Call" ) func (ext *ExtendedConn) HandleError(error) {} @@ -85,6 +86,8 @@ func (ext *ExtendedConn) HandleJsonMessage(message string) { ext.handleMessageCommand(msg[1]) case MessageChat: ext.handleMessageChatUpdate(msg[1]) + case MessageCall: + ext.handleMessageCall(msg[1]) default: for _, handler := range ext.handlers { ujmHandler, ok := handler.(UnhandledJSONMessageHandler)