From 2f506425c25504662f50f49e742529195d63ff27 Mon Sep 17 00:00:00 2001 From: Wim Date: Mon, 24 Aug 2020 23:35:08 +0200 Subject: [PATCH] Update whatsapp vendor and fix a panic (#1209) * Fix another whatsapp panic * Update whatsapp vendor --- bridge/whatsapp/handlers.go | 2 +- go.mod | 2 +- go.sum | 4 +- .../github.com/Rhymen/go-whatsapp/README.md | 4 + vendor/github.com/Rhymen/go-whatsapp/conn.go | 14 +- .../github.com/Rhymen/go-whatsapp/errors.go | 2 + .../github.com/Rhymen/go-whatsapp/handler.go | 23 +++ vendor/github.com/Rhymen/go-whatsapp/media.go | 23 +-- .../github.com/Rhymen/go-whatsapp/message.go | 136 +++++++++++++++++- vendor/github.com/Rhymen/go-whatsapp/read.go | 11 +- .../github.com/Rhymen/go-whatsapp/session.go | 24 +++- vendor/github.com/Rhymen/go-whatsapp/write.go | 8 ++ vendor/modules.txt | 2 +- 13 files changed, 228 insertions(+), 27 deletions(-) diff --git a/bridge/whatsapp/handlers.go b/bridge/whatsapp/handlers.go index 9adb1eb0..f58b30f8 100644 --- a/bridge/whatsapp/handlers.go +++ b/bridge/whatsapp/handlers.go @@ -78,7 +78,7 @@ func (b *Bwhatsapp) HandleTextMessage(message whatsapp.TextMessage) { senderJID := message.Info.SenderJid if len(senderJID) == 0 { // TODO workaround till https://github.com/Rhymen/go-whatsapp/issues/86 resolved - if message.Info.Source != nil { + if message.Info.Source != nil && message.Info.Source.Participant != nil { senderJID = *message.Info.Source.Participant } } diff --git a/go.mod b/go.mod index b3c277c6..64be9a2b 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ require ( github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f github.com/Jeffail/gabs v1.1.1 // indirect github.com/Philipp15b/go-steam v1.0.1-0.20190816133340-b04c5a83c1c0 - github.com/Rhymen/go-whatsapp v0.1.1-0.20200421062035-31e8111ac334 + github.com/Rhymen/go-whatsapp v0.1.1-0.20200818115958-f07a700b9819 github.com/d5/tengo/v2 v2.6.0 github.com/davecgh/go-spew v1.1.1 github.com/fsnotify/fsnotify v1.4.9 diff --git a/go.sum b/go.sum index de1929b5..7eb4e858 100644 --- a/go.sum +++ b/go.sum @@ -43,8 +43,8 @@ github.com/PaulARoy/azurestoragecache v0.0.0-20170906084534-3c249a3ba788/go.mod github.com/Philipp15b/go-steam v1.0.1-0.20190816133340-b04c5a83c1c0 h1:TO7d4rocnNFng6ZQrPe7U6WqHtK5eHEMrgrnnM/72IQ= github.com/Philipp15b/go-steam v1.0.1-0.20190816133340-b04c5a83c1c0/go.mod h1:HuVM+sZFzumUdKPWiz+IlCMb4RdsKdT3T+nQBKL+sYg= github.com/Rhymen/go-whatsapp v0.0.0/go.mod h1:rdQr95g2C1xcOfM7QGOhza58HeI3I+tZ/bbluv7VazA= -github.com/Rhymen/go-whatsapp v0.1.1-0.20200421062035-31e8111ac334 h1:kb1zvD+xd+XbPUdQ0lMxnRaQ76N5C9vMAClLi8Dyw1Y= -github.com/Rhymen/go-whatsapp v0.1.1-0.20200421062035-31e8111ac334/go.mod h1:o7jjkvKnigfu432dMbQ/w4PH0Yp5u4Y6ysCNjUlcYCk= +github.com/Rhymen/go-whatsapp v0.1.1-0.20200818115958-f07a700b9819 h1:LthbEFUDcL9ZSRIs9m9JjThBSKrW6aIj8YGIT7G/SSk= +github.com/Rhymen/go-whatsapp v0.1.1-0.20200818115958-f07a700b9819/go.mod h1:o7jjkvKnigfu432dMbQ/w4PH0Yp5u4Y6ysCNjUlcYCk= github.com/Rhymen/go-whatsapp/examples/echo v0.0.0-20190325075644-cc2581bbf24d/go.mod h1:zgCiQtBtZ4P4gFWvwl9aashsdwOcbb/EHOGRmSzM8ME= github.com/Rhymen/go-whatsapp/examples/restoreSession v0.0.0-20190325075644-cc2581bbf24d/go.mod h1:5sCUSpG616ZoSJhlt9iBNI/KXBqrVLcNUJqg7J9+8pU= github.com/Rhymen/go-whatsapp/examples/sendImage v0.0.0-20190325075644-cc2581bbf24d/go.mod h1:RdiyhanVEGXTam+mZ3k6Y3VDCCvXYCwReOoxGozqhHw= diff --git a/vendor/github.com/Rhymen/go-whatsapp/README.md b/vendor/github.com/Rhymen/go-whatsapp/README.md index 5f439d55..f3c7ef58 100644 --- a/vendor/github.com/Rhymen/go-whatsapp/README.md +++ b/vendor/github.com/Rhymen/go-whatsapp/README.md @@ -70,6 +70,10 @@ func (myHandler) HandleContactMessage(message whatsapp.ContactMessage) { fmt.Println(message) } +func (myHandler) HandleBatteryMessage(msg whatsapp.BatteryMessage) { + fmt.Println(message) +} + wac.AddHandler(myHandler{}) ``` The message handlers are all optional, you don't need to implement anything but the error handler to implement the interface. The ImageMessage, VideoMessage, AudioMessage and DocumentMessage provide a Download function to get the media data. diff --git a/vendor/github.com/Rhymen/go-whatsapp/conn.go b/vendor/github.com/Rhymen/go-whatsapp/conn.go index f8b57443..59445591 100644 --- a/vendor/github.com/Rhymen/go-whatsapp/conn.go +++ b/vendor/github.com/Rhymen/go-whatsapp/conn.go @@ -88,6 +88,8 @@ type Conn struct { Store *Store ServerLastSeen time.Time + timeTag string // last 3 digits obtained after a successful login takeover + longClientName string shortClientName string clientVersion string @@ -156,8 +158,8 @@ func (wac *Conn) connect() (err error) { }() dialer := &websocket.Dialer{ - ReadBufferSize: 25 * 1024 * 1024, - WriteBufferSize: 10 * 1024 * 1024, + ReadBufferSize: 0, + WriteBufferSize: 0, HandshakeTimeout: wac.msgTimeout, Proxy: wac.Proxy, } @@ -246,3 +248,11 @@ func (wac *Conn) keepAlive(minIntervalMs int, maxIntervalMs int) { } } } + +func (wac *Conn) GetConnected() bool { + return wac.connected +} + +func (wac *Conn) GetLoggedIn() bool { + return wac.loggedIn +} diff --git a/vendor/github.com/Rhymen/go-whatsapp/errors.go b/vendor/github.com/Rhymen/go-whatsapp/errors.go index b49960f8..be78ea0f 100644 --- a/vendor/github.com/Rhymen/go-whatsapp/errors.go +++ b/vendor/github.com/Rhymen/go-whatsapp/errors.go @@ -2,6 +2,7 @@ package whatsapp import ( "fmt" + "github.com/pkg/errors" ) @@ -20,6 +21,7 @@ var ( ErrServerRespondedWith404 = errors.New("server responded with status 404") ErrMediaDownloadFailedWith404 = errors.New("download failed with status code 404") ErrMediaDownloadFailedWith410 = errors.New("download failed with status code 410") + ErrInvalidWebsocket = errors.New("invalid websocket") ) type ErrConnectionFailed struct { diff --git a/vendor/github.com/Rhymen/go-whatsapp/handler.go b/vendor/github.com/Rhymen/go-whatsapp/handler.go index 1188f794..86ea43b1 100644 --- a/vendor/github.com/Rhymen/go-whatsapp/handler.go +++ b/vendor/github.com/Rhymen/go-whatsapp/handler.go @@ -133,6 +133,14 @@ type ChatListHandler interface { HandleChatList(contacts []Chat) } +/** +The BatteryMessageHandler interface needs to be implemented to receive percentage the device connected dispatched by the dispatcher. +*/ +type BatteryMessageHandler interface { + Handler + HandleBatteryMessage(battery BatteryMessage) +} + /* AddHandler adds an handler to the list of handler that receive dispatched messages. The provided handler must at least implement the Handler interface. Additionally implemented @@ -285,6 +293,17 @@ func (wac *Conn) handleWithCustomHandlers(message interface{}, handlers []Handle } } } + + case BatteryMessage: + for _, h := range handlers { + if x, ok := h.(BatteryMessageHandler); ok { + if wac.shouldCallSynchronously(h) { + x.HandleBatteryMessage(m) + } else { + go x.HandleBatteryMessage(m) + } + } + } case *proto.WebMessageInfo: for _, h := range handlers { @@ -379,6 +398,10 @@ func (wac *Conn) dispatch(msg interface{}) { wac.handle(ParseProtoMessage(v)) } } + } else if con, ok := message.Content.([]binary.Node); ok { + for a := range con { + wac.handle(ParseNodeMessage(con[a])) + } } } else if message.Description == "response" && message.Attributes["type"] == "contacts" { wac.updateContacts(message.Content) diff --git a/vendor/github.com/Rhymen/go-whatsapp/media.go b/vendor/github.com/Rhymen/go-whatsapp/media.go index 87827d9c..43ff424f 100644 --- a/vendor/github.com/Rhymen/go-whatsapp/media.go +++ b/vendor/github.com/Rhymen/go-whatsapp/media.go @@ -93,18 +93,21 @@ func downloadMedia(url string) (file []byte, mac []byte, err error) { return data[:n-10], data[n-10 : n], nil } -type MediaConn struct { - Status int `json:"status"` - MediaConn struct { - Auth string `json:"auth"` - TTL int `json:"ttl"` - Hosts []struct { - Hostname string `json:"hostname"` - IPs []string `json:"ips"` - } `json:"hosts"` - } `json:"media_conn"` + +type MediaConn struct { + Status int `json:"status"` + MediaConn struct { + Auth string `json:"auth"` + TTL int `json:"ttl"` + Hosts []struct { + Hostname string `json:"hostname"` + IPs []interface{} `json:"ips"` + } `json:"hosts"` + } `json:"media_conn"` } + + func (wac *Conn) queryMediaConn() (hostname, auth string, ttl int, err error) { queryReq := []interface{}{"query", "mediaConn"} ch, err := wac.writeJson(queryReq) diff --git a/vendor/github.com/Rhymen/go-whatsapp/message.go b/vendor/github.com/Rhymen/go-whatsapp/message.go index 77e7e1c3..d4bc0926 100644 --- a/vendor/github.com/Rhymen/go-whatsapp/message.go +++ b/vendor/github.com/Rhymen/go-whatsapp/message.go @@ -81,7 +81,7 @@ func (wac *Conn) Send(msg interface{}) (string, error) { return "ERROR", fmt.Errorf("error decoding sending response: %v\n", err) } if int(resp["status"].(float64)) != 200 { - return "ERROR", fmt.Errorf("message sending responded with %d", resp["status"]) + return "ERROR", fmt.Errorf("message sending responded with %v", resp["status"]) } if int(resp["status"].(float64)) == 200 { return getMessageInfo(msgProto).Id, nil @@ -105,6 +105,105 @@ func (wac *Conn) sendProto(p *proto.WebMessageInfo) (<-chan string, error) { return wac.writeBinary(n, message, ignore, p.Key.GetId()) } +// RevokeMessage revokes a message (marks as "message removed") for everyone +func (wac *Conn) RevokeMessage(remotejid, msgid string, fromme bool) (revokeid string, err error) { + // create a revocation ID (required) + rawrevocationID := make([]byte, 10) + rand.Read(rawrevocationID) + revocationID := strings.ToUpper(hex.EncodeToString(rawrevocationID)) + // + ts := uint64(time.Now().Unix()) + status := proto.WebMessageInfo_PENDING + mtype := proto.ProtocolMessage_REVOKE + + revoker := &proto.WebMessageInfo{ + Key: &proto.MessageKey{ + FromMe: &fromme, + Id: &revocationID, + RemoteJid: &remotejid, + }, + MessageTimestamp: &ts, + Message: &proto.Message{ + ProtocolMessage: &proto.ProtocolMessage{ + Type: &mtype, + Key: &proto.MessageKey{ + FromMe: &fromme, + Id: &msgid, + RemoteJid: &remotejid, + }, + }, + }, + Status: &status, + } + if _, err := wac.Send(revoker); err != nil { + return revocationID, err + } + return revocationID, nil +} + +// DeleteMessage deletes a single message for the user (removes the msgbox). To +// delete the message for everyone, use RevokeMessage +func (wac *Conn) DeleteMessage(remotejid, msgid string, fromMe bool) error { + ch, err := wac.deleteChatProto(remotejid, msgid, fromMe) + if err != nil { + return fmt.Errorf("could not send proto: %v", err) + } + + select { + case response := <-ch: + var resp map[string]interface{} + if err = json.Unmarshal([]byte(response), &resp); err != nil { + return fmt.Errorf("error decoding deletion response: %v", err) + } + if int(resp["status"].(float64)) != 200 { + return fmt.Errorf("message deletion responded with %v", resp["status"]) + } + if int(resp["status"].(float64)) == 200 { + return nil + } + case <-time.After(wac.msgTimeout): + return fmt.Errorf("deleting message timed out") + } + + return nil +} + +func (wac *Conn) deleteChatProto(remotejid, msgid string, fromMe bool) (<-chan string, error) { + tag := fmt.Sprintf("%s.--%d", wac.timeTag, wac.msgCount) + + owner := "true" + if !fromMe { + owner = "false" + } + n := binary.Node{ + Description: "action", + Attributes: map[string]string{ + "epoch": strconv.Itoa(wac.msgCount), + "type": "set", + }, + Content: []interface{}{ + binary.Node{ + Description: "chat", + Attributes: map[string]string{ + "type": "clear", + "jid": remotejid, + "media": "true", + }, + Content: []binary.Node{ + { + Description: "item", + Attributes: map[string]string{ + "owner": owner, + "index": msgid, + }, + }, + }, + }, + }, + } + return wac.writeBinary(n, chat, expires|skipOffline, tag) +} + func init() { rand.Seed(time.Now().UTC().UnixNano()) } @@ -744,3 +843,38 @@ func ParseProtoMessage(msg *proto.WebMessageInfo) interface{} { return nil } + + +/* +BatteryMessage represents a battery level and charging state. +*/ +type BatteryMessage struct { + Plugged bool + Powersave bool + Percentage int +} + +func getBatteryMessage(msg map[string]string) BatteryMessage { + plugged, _ := strconv.ParseBool(msg["live"]) + powersave, _ := strconv.ParseBool(msg["powersave"]) + percentage, _ := strconv.Atoi(msg["value"]) + batteryMessage := BatteryMessage{ + Plugged: plugged, + Powersave: powersave, + Percentage: percentage, + } + + return batteryMessage +} + + +func ParseNodeMessage(msg binary.Node) interface{} { + switch msg.Description { + case "battery": + return getBatteryMessage(msg.Attributes) + default: + //cannot match message + } + + return nil +} diff --git a/vendor/github.com/Rhymen/go-whatsapp/read.go b/vendor/github.com/Rhymen/go-whatsapp/read.go index b870f5f8..c35f7f0e 100644 --- a/vendor/github.com/Rhymen/go-whatsapp/read.go +++ b/vendor/github.com/Rhymen/go-whatsapp/read.go @@ -5,13 +5,14 @@ import ( "crypto/sha256" "encoding/json" "fmt" + "io" + "io/ioutil" + "strings" + "github.com/Rhymen/go-whatsapp/binary" "github.com/Rhymen/go-whatsapp/crypto/cbc" "github.com/gorilla/websocket" "github.com/pkg/errors" - "io" - "io/ioutil" - "strings" ) func (wac *Conn) readPump() { @@ -27,7 +28,9 @@ func (wac *Conn) readPump() { for { readerFound := make(chan struct{}) go func() { - msgType, reader, readErr = wac.ws.conn.NextReader() + if wac.ws != nil { + msgType, reader, readErr = wac.ws.conn.NextReader() + } close(readerFound) }() select { diff --git a/vendor/github.com/Rhymen/go-whatsapp/session.go b/vendor/github.com/Rhymen/go-whatsapp/session.go index f026df7d..dc3acd57 100644 --- a/vendor/github.com/Rhymen/go-whatsapp/session.go +++ b/vendor/github.com/Rhymen/go-whatsapp/session.go @@ -18,7 +18,7 @@ import ( ) //represents the WhatsAppWeb client version -var waVersion = []int{0, 4, 2080} +var waVersion = []int{2, 2033, 7} /* Session contains session individual information. To be able to resume the connection without scanning the qr code @@ -141,11 +141,11 @@ func CheckCurrentServerVersion() ([]int, error) { SetClientName sets the long and short client names that are sent to WhatsApp when logging in and displayed in the WhatsApp Web device list. As the values are only sent when logging in, changing them after logging in is not possible. */ -func (wac *Conn) SetClientName(long, short, version string) error { +func (wac *Conn) SetClientName(long, short string) error { if wac.session != nil && (wac.session.EncKey != nil || wac.session.MacKey != nil) { return fmt.Errorf("cannot change client name after logging in") } - wac.longClientName, wac.shortClientName, wac.clientVersion = long, short, version + wac.longClientName, wac.shortClientName = long, short return nil } @@ -231,7 +231,12 @@ func (wac *Conn) Login(qrChan chan<- string) (Session, error) { return session, fmt.Errorf("error decoding login resp: %v\n", err) } - ref := resp["ref"].(string) + var ref string + if rref, ok := resp["ref"].(string); ok { + ref = rref + } else { + return session, fmt.Errorf("error decoding login resp: invalid resp['ref']\n") + } priv, pub, err := curve25519.GenerateKey() if err != nil { @@ -390,9 +395,11 @@ func (wac *Conn) Restore() error { } if int(resp["status"].(float64)) != 200 { + wac.timeTag = "" return fmt.Errorf("init responded with %d", resp["status"]) } case <-time.After(wac.msgTimeout): + wac.timeTag = "" return fmt.Errorf("restore session init timed out") } @@ -401,10 +408,11 @@ func (wac *Conn) Restore() error { select { case r1 := <-s1: if err := json.Unmarshal([]byte(r1), &connResp); err != nil { + wac.timeTag = "" return fmt.Errorf("error decoding s1 message: %v\n", err) } case <-time.After(wac.msgTimeout): - + wac.timeTag = "" //check for an error message select { case r := <-loginChan: @@ -429,15 +437,18 @@ func (wac *Conn) Restore() error { wac.listener.Unlock() if err := wac.resolveChallenge(connResp[1].(map[string]interface{})["challenge"].(string)); err != nil { + wac.timeTag = "" return fmt.Errorf("error resolving challenge: %v\n", err) } select { case r := <-s2: if err := json.Unmarshal([]byte(r), &connResp); err != nil { + wac.timeTag = "" return fmt.Errorf("error decoding s2 message: %v\n", err) } case <-time.After(wac.msgTimeout): + wac.timeTag = "" return fmt.Errorf("restore session challenge timed out") } } @@ -447,13 +458,16 @@ func (wac *Conn) Restore() error { case r := <-loginChan: var resp map[string]interface{} if err = json.Unmarshal([]byte(r), &resp); err != nil { + wac.timeTag = "" return fmt.Errorf("error decoding login connResp: %v\n", err) } if int(resp["status"].(float64)) != 200 { + wac.timeTag = "" return fmt.Errorf("admin login responded with %d", resp["status"]) } case <-time.After(wac.msgTimeout): + wac.timeTag = "" return fmt.Errorf("restore session login timed out") } diff --git a/vendor/github.com/Rhymen/go-whatsapp/write.go b/vendor/github.com/Rhymen/go-whatsapp/write.go index 1a2f4d7b..74f43c42 100644 --- a/vendor/github.com/Rhymen/go-whatsapp/write.go +++ b/vendor/github.com/Rhymen/go-whatsapp/write.go @@ -30,6 +30,11 @@ func (wac *Conn) writeJson(data []interface{}) (<-chan string, error) { messageTag := fmt.Sprintf("%d.--%d", ts, wac.msgCount) bytes := []byte(fmt.Sprintf("%s,%s", messageTag, d)) + if wac.timeTag == "" { + tss := fmt.Sprintf("%d", ts) + wac.timeTag = tss[len(tss)-3:] + } + ch, err := wac.write(websocket.TextMessage, messageTag, bytes) if err != nil { return nil, err @@ -127,6 +132,9 @@ func (wac *Conn) write(messageType int, answerMessageTag string, data []byte) (< wac.listener.Unlock() } + if wac == nil || wac.ws == nil { + return nil, ErrInvalidWebsocket + } wac.ws.Lock() err := wac.ws.conn.WriteMessage(messageType, data) wac.ws.Unlock() diff --git a/vendor/modules.txt b/vendor/modules.txt index da0774ff..da794c6c 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -15,7 +15,7 @@ github.com/Philipp15b/go-steam/protocol/steamlang github.com/Philipp15b/go-steam/rwu github.com/Philipp15b/go-steam/socialcache github.com/Philipp15b/go-steam/steamid -# github.com/Rhymen/go-whatsapp v0.1.1-0.20200421062035-31e8111ac334 +# github.com/Rhymen/go-whatsapp v0.1.1-0.20200818115958-f07a700b9819 github.com/Rhymen/go-whatsapp github.com/Rhymen/go-whatsapp/binary github.com/Rhymen/go-whatsapp/binary/proto