Browse Source

Add JSONMessage parsing to whatsapp extensions

Tulir Asokan 7 years ago
parent
commit
d13bf4ae64

+ 59 - 0
whatsapp-ext/conn.go

@@ -0,0 +1,59 @@
+// mautrix-whatsapp - A Matrix-WhatsApp puppeting bridge.
+// Copyright (C) 2018 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 <https://www.gnu.org/licenses/>.
+
+package whatsapp_ext
+
+import (
+	"github.com/Rhymen/go-whatsapp"
+	"encoding/json"
+)
+
+type ConnInfo struct {
+	ProtocolVersion []int `json:"protoVersion"`
+	BinaryVersion   int   `json:"binVersion"`
+	Phone           struct {
+		WhatsAppVersion    string `json:"wa_version"`
+		MCC                int    `json:"mcc"`
+		MNC                int    `json:"mnc"`
+		OSVersion          string `json:"os_version"`
+		DeviceManufacturer string `json:"device_manufacturer"`
+		DeviceModel        string `json:"device_model"`
+		OSBuildNumber      string `json:"os_build_number"`
+	} `json:"phone"`
+	Features map[string]interface{} `json:"features"`
+	PushName string                 `json:"pushname"`
+}
+
+type ConnInfoHandler interface {
+	whatsapp.Handler
+	HandleConnInfo(ConnInfo)
+}
+
+func (ext *ExtendedConn) handleMessageConn(message []byte) {
+	var event ConnInfo
+	err := json.Unmarshal(message, &event)
+	if err != nil {
+		ext.jsonParseError(err)
+		return
+	}
+	for _, handler := range ext.handlers {
+		connInfoHandler, ok := handler.(ConnInfoHandler)
+		if !ok {
+			continue
+		}
+		connInfoHandler.HandleConnInfo(event)
+	}
+}

+ 93 - 0
whatsapp-ext/jsonmessage.go

@@ -0,0 +1,93 @@
+// mautrix-whatsapp - A Matrix-WhatsApp puppeting bridge.
+// Copyright (C) 2018 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 <https://www.gnu.org/licenses/>.
+
+package whatsapp_ext
+
+import (
+	"encoding/json"
+	"github.com/Rhymen/go-whatsapp"
+)
+
+type JSONMessage []json.RawMessage
+
+type JSONMessageType string
+
+const (
+	MessageMsgInfo  JSONMessageType = "MsgInfo"
+	MessagePresence JSONMessageType = "Presence"
+	MessageStream   JSONMessageType = "Stream"
+	MessageConn     JSONMessageType = "Conn"
+	MessageProps    JSONMessageType = "Props"
+)
+
+func (ext *ExtendedConn) AddHandler(handler whatsapp.Handler) {
+	ext.Conn.AddHandler(handler)
+	ext.handlers = append(ext.handlers, handler)
+}
+
+func (ext *ExtendedConn) HandleError(error) {}
+
+type UnhandledJSONMessageHandler interface {
+	whatsapp.Handler
+	HandleUnhandledJSONMessage(string)
+}
+
+type JSONParseErrorHandler interface {
+	whatsapp.Handler
+	HandleJSONParseError(error)
+}
+
+func (ext *ExtendedConn) jsonParseError(err error) {
+	for _, handler := range ext.handlers {
+		errorHandler, ok := handler.(JSONParseErrorHandler)
+		if !ok {
+			continue
+		}
+		errorHandler.HandleJSONParseError(err)
+	}
+}
+
+func (ext *ExtendedConn) HandleJsonMessage(message string) {
+	msg := JSONMessage{}
+	err := json.Unmarshal([]byte(message), &msg)
+	if err != nil || len(msg) < 2 {
+		return
+	}
+
+	var msgType JSONMessageType
+	json.Unmarshal(msg[0], &msgType)
+
+	switch msgType {
+	case MessagePresence:
+		ext.handleMessagePresence(msg[1])
+	case MessageStream:
+		ext.handleMessageStream(msg[1:])
+	case MessageConn:
+		ext.handleMessageProps(msg[1])
+	case MessageProps:
+		ext.handleMessageProps(msg[1])
+	case MessageMsgInfo:
+		ext.handleMessageMsgInfo(msg[1])
+	default:
+		for _, handler := range ext.handlers {
+			ujmHandler, ok := handler.(UnhandledJSONMessageHandler)
+			if !ok {
+				continue
+			}
+			ujmHandler.HandleUnhandledJSONMessage(message)
+		}
+	}
+}

+ 63 - 0
whatsapp-ext/msginfo.go

@@ -0,0 +1,63 @@
+// mautrix-whatsapp - A Matrix-WhatsApp puppeting bridge.
+// Copyright (C) 2018 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 <https://www.gnu.org/licenses/>.
+
+package whatsapp_ext
+
+import (
+	"github.com/Rhymen/go-whatsapp"
+	"encoding/json"
+	"strings"
+)
+
+type MsgInfoCommand string
+
+const (
+	MsgInfoCommandAcknowledge MsgInfoCommand = "ack"
+)
+
+type MsgInfo struct {
+	Command         MsgInfoCommand `json:"cmd"`
+	ID              string         `json:"id"`
+	Acknowledgement int            `json:"ack"`
+	MessageFromJID  string         `json:"from"`
+	SenderJID       string         `json:"participant"`
+	ToJID           string         `json:"to"`
+	Timestamp       int64          `json:"t"`
+}
+
+type MsgInfoHandler interface {
+	whatsapp.Handler
+	HandleMsgInfo(MsgInfo)
+}
+
+func (ext *ExtendedConn) handleMessageMsgInfo(message []byte) {
+	var event MsgInfo
+	err := json.Unmarshal(message, &event)
+	if err != nil {
+		ext.jsonParseError(err)
+		return
+	}
+	event.MessageFromJID = strings.Replace(event.MessageFromJID, OldUserSuffix, NewUserSuffix, 1)
+	event.SenderJID = strings.Replace(event.SenderJID, OldUserSuffix, NewUserSuffix, 1)
+	event.ToJID = strings.Replace(event.ToJID, OldUserSuffix, NewUserSuffix, 1)
+	for _, handler := range ext.handlers {
+		msgInfoHandler, ok := handler.(MsgInfoHandler)
+		if !ok {
+			continue
+		}
+		msgInfoHandler.HandleMsgInfo(event)
+	}
+}

+ 58 - 0
whatsapp-ext/presence.go

@@ -0,0 +1,58 @@
+// mautrix-whatsapp - A Matrix-WhatsApp puppeting bridge.
+// Copyright (C) 2018 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 <https://www.gnu.org/licenses/>.
+
+package whatsapp_ext
+
+import (
+	"github.com/Rhymen/go-whatsapp"
+	"encoding/json"
+	"strings"
+)
+
+type PresenceType string
+
+const (
+	PresenceUnavailable PresenceType = "unavailable"
+	PresenceAvailable   PresenceType = "available"
+)
+
+type Presence struct {
+	JID       string       `json:"id"`
+	Status    PresenceType `json:"type"`
+	Timestamp int64        `json:"t"`
+}
+
+type PresenceHandler interface {
+	whatsapp.Handler
+	HandlePresence(Presence)
+}
+
+func (ext *ExtendedConn) handleMessagePresence(message []byte) {
+	var event Presence
+	err := json.Unmarshal(message, &event)
+	if err != nil {
+		ext.jsonParseError(err)
+		return
+	}
+	event.JID = strings.Replace(event.JID, OldUserSuffix, NewUserSuffix, 1)
+	for _, handler := range ext.handlers {
+		presenceHandler, ok := handler.(PresenceHandler)
+		if !ok {
+			continue
+		}
+		presenceHandler.HandlePresence(event)
+	}
+}

+ 67 - 0
whatsapp-ext/props.go

@@ -0,0 +1,67 @@
+// mautrix-whatsapp - A Matrix-WhatsApp puppeting bridge.
+// Copyright (C) 2018 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 <https://www.gnu.org/licenses/>.
+
+package whatsapp_ext
+
+import (
+	"github.com/Rhymen/go-whatsapp"
+	"encoding/json"
+)
+
+type ProtocolProps struct {
+	WebPresence            bool   `json:"webPresence"`
+	NotificationQuery      bool   `json:"notificationQuery"`
+	FacebookCrashLog       bool   `json:"fbCrashlog"`
+	Bucket                 string `json:"bucket"`
+	GIFSearch              string `json:"gifSearch"`
+	Spam                   bool   `json:"SPAM"`
+	SetBlock               bool   `json:"SET_BLOCK"`
+	MessageInfo            bool   `json:"MESSAGE_INFO"`
+	MaxFileSize            int    `json:"maxFileSize"`
+	Media                  int    `json:"media"`
+	GroupNameLength        int    `json:"maxSubject"`
+	GroupDescriptionLength int    `json:"groupDescLength"`
+	MaxParticipants        int    `json:"maxParticipants"`
+	VideoMaxEdge           int    `json:"videoMaxEdge"`
+	ImageMaxEdge           int    `json:"imageMaxEdge"`
+	ImageMaxKilobytes      int    `json:"imageMaxKBytes"`
+	Edit                   int    `json:"edit"`
+	FwdUIStartTimestamp    int    `json:"fwdUiStartTs"`
+	GroupsV3               int    `json:"groupsV3"`
+	RestrictGroups         int    `json:"restrictGroups"`
+	AnnounceGroups         int    `json:"announceGroups"`
+}
+
+type ProtocolPropsHandler interface {
+	whatsapp.Handler
+	HandleProtocolProps(ProtocolProps)
+}
+
+func (ext *ExtendedConn) handleMessageProps(message []byte) {
+	var event ProtocolProps
+	err := json.Unmarshal(message, &event)
+	if err != nil {
+		ext.jsonParseError(err)
+		return
+	}
+	for _, handler := range ext.handlers {
+		protocolPropsHandler, ok := handler.(ProtocolPropsHandler)
+		if !ok {
+			continue
+		}
+		protocolPropsHandler.HandleProtocolProps(event)
+	}
+}

+ 62 - 0
whatsapp-ext/stream.go

@@ -0,0 +1,62 @@
+// mautrix-whatsapp - A Matrix-WhatsApp puppeting bridge.
+// Copyright (C) 2018 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 <https://www.gnu.org/licenses/>.
+
+package whatsapp_ext
+
+import (
+	"encoding/json"
+	"github.com/Rhymen/go-whatsapp"
+)
+
+type StreamType string
+
+const (
+	StreamUpdate = "update"
+	StreamSleep  = "asleep"
+)
+
+type StreamEvent struct {
+	Type    StreamType
+	Boolean bool
+	Version string
+}
+
+type StreamEventHandler interface {
+	whatsapp.Handler
+	HandleStreamEvent(StreamEvent)
+}
+
+func (ext *ExtendedConn) handleMessageStream(message []json.RawMessage) {
+	var event StreamEvent
+	err := json.Unmarshal(message[0], &event.Type)
+	if err != nil {
+		ext.jsonParseError(err)
+		return
+	}
+
+	if event.Type == StreamUpdate && len(message) > 4 {
+		json.Unmarshal(message[1], event.Boolean)
+		json.Unmarshal(message[2], event.Version)
+	}
+
+	for _, handler := range ext.handlers {
+		streamHandler, ok := handler.(StreamEventHandler)
+		if !ok {
+			continue
+		}
+		streamHandler.HandleStreamEvent(event)
+	}
+}

+ 12 - 3
whatsapp-ext/whatsapp.go

@@ -26,14 +26,23 @@ import (
 	"strings"
 )
 
+const (
+	OldUserSuffix = "@c.us"
+	NewUserSuffix = "@s.whatsapp.net"
+)
+
 type ExtendedConn struct {
 	*whatsapp.Conn
+
+	handlers []whatsapp.Handler
 }
 
 func ExtendConn(conn *whatsapp.Conn) *ExtendedConn {
-	return &ExtendedConn{
+	ext := &ExtendedConn{
 		Conn: conn,
 	}
+	ext.Conn.AddHandler(ext)
+	return ext
 }
 
 type GroupInfo struct {
@@ -64,7 +73,7 @@ func (ext *ExtendedConn) GetGroupMetaData(jid string) (*GroupInfo, error) {
 		return nil, fmt.Errorf("failed to get group metadata: %v", err)
 	}
 	content := <-data
-	fmt.Println("GROUP METADATA", content)
+
 	info := &GroupInfo{}
 	err = json.Unmarshal([]byte(content), info)
 	if err != nil {
@@ -72,7 +81,7 @@ func (ext *ExtendedConn) GetGroupMetaData(jid string) (*GroupInfo, error) {
 	}
 
 	for index, participant := range info.Participants {
-		info.Participants[index].JID = strings.Replace(participant.JID, "@c.us", "@s.whatsapp.net", 1)
+		info.Participants[index].JID = strings.Replace(participant.JID, OldUserSuffix, NewUserSuffix, 1)
 	}
 
 	return info, nil