diff --git a/whatsapp-ext/conn.go b/whatsapp-ext/conn.go new file mode 100644 index 0000000..37c5192 --- /dev/null +++ b/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 . + +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) + } +} diff --git a/whatsapp-ext/jsonmessage.go b/whatsapp-ext/jsonmessage.go new file mode 100644 index 0000000..be44375 --- /dev/null +++ b/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 . + +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) + } + } +} diff --git a/whatsapp-ext/msginfo.go b/whatsapp-ext/msginfo.go new file mode 100644 index 0000000..f63f454 --- /dev/null +++ b/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 . + +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) + } +} diff --git a/whatsapp-ext/presence.go b/whatsapp-ext/presence.go new file mode 100644 index 0000000..57a6507 --- /dev/null +++ b/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 . + +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) + } +} diff --git a/whatsapp-ext/props.go b/whatsapp-ext/props.go new file mode 100644 index 0000000..ecf847a --- /dev/null +++ b/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 . + +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) + } +} diff --git a/whatsapp-ext/stream.go b/whatsapp-ext/stream.go new file mode 100644 index 0000000..5496df7 --- /dev/null +++ b/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 . + +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) + } +} diff --git a/whatsapp-ext/whatsapp.go b/whatsapp-ext/whatsapp.go index 4941c64..649c1a7 100644 --- a/whatsapp-ext/whatsapp.go +++ b/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