From d525f1c9e4ac39b74575052b61c4de72de794b4e Mon Sep 17 00:00:00 2001 From: Wim Date: Mon, 26 Aug 2019 23:22:34 +0200 Subject: Update Rhymen/go-whatsapp vendor (#876) --- vendor/github.com/Rhymen/go-whatsapp/README.md | 10 +- .../github.com/Rhymen/go-whatsapp/chat_history.go | 183 +++++++++++++++++++++ vendor/github.com/Rhymen/go-whatsapp/conn.go | 15 ++ vendor/github.com/Rhymen/go-whatsapp/errors.go | 21 ++- vendor/github.com/Rhymen/go-whatsapp/handler.go | 128 +++++++++++--- vendor/github.com/Rhymen/go-whatsapp/media.go | 12 +- vendor/github.com/Rhymen/go-whatsapp/message.go | 107 +++++++++++- vendor/github.com/Rhymen/go-whatsapp/read.go | 25 ++- vendor/github.com/Rhymen/go-whatsapp/session.go | 10 ++ vendor/github.com/Rhymen/go-whatsapp/write.go | 30 ++++ 10 files changed, 505 insertions(+), 36 deletions(-) create mode 100644 vendor/github.com/Rhymen/go-whatsapp/chat_history.go (limited to 'vendor/github.com/Rhymen/go-whatsapp') diff --git a/vendor/github.com/Rhymen/go-whatsapp/README.md b/vendor/github.com/Rhymen/go-whatsapp/README.md index b90d2991..1248b8f9 100644 --- a/vendor/github.com/Rhymen/go-whatsapp/README.md +++ b/vendor/github.com/Rhymen/go-whatsapp/README.md @@ -50,17 +50,25 @@ func (myHandler) HandleImageMessage(message whatsapp.ImageMessage) { fmt.Println(message) } +func (myHandler) HandleDocumentMessage(message whatsapp.DocumentMessage) { + fmt.Println(message) +} + func (myHandler) HandleVideoMessage(message whatsapp.VideoMessage) { fmt.Println(message) } +func (myHandler) HandleAudioMessage(message whatsapp.AudioMessage){ + fmt.Println(message) +} + func (myHandler) HandleJsonMessage(message string) { 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 and VideoMessage provide a Download function to get the media data. +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. ### Sending text messages ```go diff --git a/vendor/github.com/Rhymen/go-whatsapp/chat_history.go b/vendor/github.com/Rhymen/go-whatsapp/chat_history.go new file mode 100644 index 00000000..55c7ae63 --- /dev/null +++ b/vendor/github.com/Rhymen/go-whatsapp/chat_history.go @@ -0,0 +1,183 @@ +package whatsapp + +import ( + "github.com/Rhymen/go-whatsapp/binary" + "github.com/Rhymen/go-whatsapp/binary/proto" + "log" + "strconv" + "time" +) + +type MessageOffsetInfo struct { + FirstMessageId string + FirstMessageOwner bool +} + +func decodeMessages(n *binary.Node) []*proto.WebMessageInfo { + + var messages = make([]*proto.WebMessageInfo, 0) + + if n == nil || n.Attributes == nil || n.Content == nil { + return messages + } + + for _, msg := range n.Content.([]interface{}) { + switch msg.(type) { + case *proto.WebMessageInfo: + messages = append(messages, msg.(*proto.WebMessageInfo)) + default: + log.Println("decodeMessages: Non WebMessage encountered") + } + } + + return messages +} + +// LoadChatMessages is useful to "scroll" messages, loading by count at a time +// if handlers == nil the func will use default handlers +// if after == true LoadChatMessages will load messages after the specified messageId, otherwise it will return +// message before the messageId +func (wac *Conn) LoadChatMessages(jid string, count int, messageId string, owner bool, after bool, handlers ...Handler) error { + if count <= 0 { + return nil + } + + if handlers == nil { + handlers = wac.handler + } + + kind := "before" + if after { + kind = "after" + } + + node, err := wac.query("message", jid, messageId, kind, + strconv.FormatBool(owner), "", count, 0) + + if err != nil { + wac.handleWithCustomHandlers(err, handlers) + return err + } + + for _, msg := range decodeMessages(node) { + wac.handleWithCustomHandlers(ParseProtoMessage(msg), handlers) + wac.handleWithCustomHandlers(msg, handlers) + } + return nil + +} + +// LoadFullChatHistory loads full chat history for the given jid +// chunkSize = how many messages to load with one query; if handlers == nil the func will use default handlers; +// pauseBetweenQueries = how much time to sleep between queries +func (wac *Conn) LoadFullChatHistory(jid string, chunkSize int, + pauseBetweenQueries time.Duration, handlers ...Handler) { + if chunkSize <= 0 { + return + } + + if handlers == nil { + handlers = wac.handler + } + + beforeMsg := "" + beforeMsgIsOwner := true + + for { + node, err := wac.query("message", jid, beforeMsg, "before", + strconv.FormatBool(beforeMsgIsOwner), "", chunkSize, 0) + + if err != nil { + wac.handleWithCustomHandlers(err, handlers) + } else { + + msgs := decodeMessages(node) + for _, msg := range msgs { + wac.handleWithCustomHandlers(ParseProtoMessage(msg), handlers) + wac.handleWithCustomHandlers(msg, handlers) + } + + if len(msgs) == 0 { + break + } + + beforeMsg = *msgs[0].Key.Id + beforeMsgIsOwner = msgs[0].Key.FromMe != nil && *msgs[0].Key.FromMe + } + + <-time.After(pauseBetweenQueries) + + } + +} + +// LoadFullChatHistoryAfter loads all messages after the specified messageId +// useful to "catch up" with the message history after some specified message +func (wac *Conn) LoadFullChatHistoryAfter(jid string, messageId string, chunkSize int, + pauseBetweenQueries time.Duration, handlers ...Handler) { + + if chunkSize <= 0 { + return + } + + if handlers == nil { + handlers = wac.handler + } + + msgOwner := true + prevNotFound := false + + for { + node, err := wac.query("message", jid, messageId, "after", + strconv.FormatBool(msgOwner), "", chunkSize, 0) + + if err != nil { + + // Whatsapp will return 404 status when there is wrong owner flag on the requested message id + if err == ErrServerRespondedWith404 { + + // this will detect two consecutive "not found" errors. + // this is done to prevent infinite loop when wrong message id supplied + if prevNotFound { + log.Println("LoadFullChatHistoryAfter: could not retrieve any messages, wrong message id?") + return + } + prevNotFound = true + + // try to reverse the owner flag and retry + if msgOwner { + // reverse initial msgOwner value and retry + msgOwner = false + + <-time.After(time.Second) + continue + } + + } + + // if the error isn't a 404 error, pass it to the error handler + wac.handleWithCustomHandlers(err, handlers) + } else { + + msgs := decodeMessages(node) + for _, msg := range msgs { + wac.handleWithCustomHandlers(ParseProtoMessage(msg), handlers) + wac.handleWithCustomHandlers(msg, handlers) + } + + if len(msgs) != chunkSize { + break + } + + messageId = *msgs[0].Key.Id + msgOwner = msgs[0].Key.FromMe != nil && *msgs[0].Key.FromMe + } + + // message was found + prevNotFound = false + + <-time.After(pauseBetweenQueries) + + } + +} diff --git a/vendor/github.com/Rhymen/go-whatsapp/conn.go b/vendor/github.com/Rhymen/go-whatsapp/conn.go index ff83d42d..2bdeb118 100644 --- a/vendor/github.com/Rhymen/go-whatsapp/conn.go +++ b/vendor/github.com/Rhymen/go-whatsapp/conn.go @@ -89,6 +89,8 @@ type Conn struct { longClientName string shortClientName string + + loginSessionLock sync.RWMutex } type websocketWrapper struct { @@ -191,6 +193,19 @@ func (wac *Conn) Disconnect() (Session, error) { return *wac.session, err } +func (wac *Conn) AdminTest() (bool, error) { + if !wac.connected { + return false, ErrNotConnected + } + + if !wac.loggedIn { + return false, ErrInvalidSession + } + + result, err := wac.sendAdminTest() + return result, err +} + func (wac *Conn) keepAlive(minIntervalMs int, maxIntervalMs int) { defer wac.wg.Done() diff --git a/vendor/github.com/Rhymen/go-whatsapp/errors.go b/vendor/github.com/Rhymen/go-whatsapp/errors.go index 793f8cc9..b505a3c4 100644 --- a/vendor/github.com/Rhymen/go-whatsapp/errors.go +++ b/vendor/github.com/Rhymen/go-whatsapp/errors.go @@ -6,15 +6,18 @@ import ( ) var ( - ErrAlreadyConnected = errors.New("already connected") - ErrAlreadyLoggedIn = errors.New("already logged in") - ErrInvalidSession = errors.New("invalid session") - ErrLoginInProgress = errors.New("login or restore already running") - ErrNotConnected = errors.New("not connected") - ErrInvalidWsData = errors.New("received invalid data") - ErrConnectionTimeout = errors.New("connection timed out") - ErrMissingMessageTag = errors.New("no messageTag specified or to short") - ErrInvalidHmac = errors.New("invalid hmac") + ErrAlreadyConnected = errors.New("already connected") + ErrAlreadyLoggedIn = errors.New("already logged in") + ErrInvalidSession = errors.New("invalid session") + ErrLoginInProgress = errors.New("login or restore already running") + ErrNotConnected = errors.New("not connected") + ErrInvalidWsData = errors.New("received invalid data") + ErrInvalidWsState = errors.New("can't handle binary data when not logged in") + ErrConnectionTimeout = errors.New("connection timed out") + ErrMissingMessageTag = errors.New("no messageTag specified or to short") + ErrInvalidHmac = errors.New("invalid hmac") + ErrInvalidServerResponse = errors.New("invalid response received from server") + ErrServerRespondedWith404 = errors.New("server responded with status 404") ) type ErrConnectionFailed struct { diff --git a/vendor/github.com/Rhymen/go-whatsapp/handler.go b/vendor/github.com/Rhymen/go-whatsapp/handler.go index f909bed5..8d4b6bee 100644 --- a/vendor/github.com/Rhymen/go-whatsapp/handler.go +++ b/vendor/github.com/Rhymen/go-whatsapp/handler.go @@ -20,6 +20,11 @@ type Handler interface { HandleError(err error) } +type SyncHandler interface { + Handler + ShouldCallSynchronously() bool +} + /* The TextMessageHandler interface needs to be implemented to receive text messages dispatched by the dispatcher. */ @@ -60,6 +65,22 @@ type DocumentMessageHandler interface { HandleDocumentMessage(message DocumentMessage) } +/* +The LiveLocationMessageHandler interface needs to be implemented to receive live location messages dispatched by the dispatcher. +*/ +type LiveLocationMessageHandler interface { + Handler + HandleLiveLocationMessage(message LiveLocationMessage) +} + +/* +The LocationMessageHandler interface needs to be implemented to receive location messages dispatched by the dispatcher. +*/ +type LocationMessageHandler interface { + Handler + HandleLocationMessage(message LocationMessage) +} + /* The JsonMessageHandler interface needs to be implemented to receive json messages dispatched by the dispatcher. These json messages contain status updates of every kind sent by WhatsAppWeb servers. WhatsAppWeb uses these messages @@ -127,52 +148,113 @@ func (wac *Conn) RemoveHandlers() { wac.handler = make([]Handler, 0) } +func (wac *Conn) shouldCallSynchronously(handler Handler) bool { + sh, ok := handler.(SyncHandler) + return ok && sh.ShouldCallSynchronously() +} + func (wac *Conn) handle(message interface{}) { + wac.handleWithCustomHandlers(message, wac.handler) +} + +func (wac *Conn) handleWithCustomHandlers(message interface{}, handlers []Handler) { switch m := message.(type) { case error: - for _, h := range wac.handler { - go h.HandleError(m) + for _, h := range handlers { + if wac.shouldCallSynchronously(h) { + h.HandleError(m) + } else { + go h.HandleError(m) + } } case string: - for _, h := range wac.handler { + for _, h := range handlers { if x, ok := h.(JsonMessageHandler); ok { - go x.HandleJsonMessage(m) + if wac.shouldCallSynchronously(h) { + x.HandleJsonMessage(m) + } else { + go x.HandleJsonMessage(m) + } } } case TextMessage: - for _, h := range wac.handler { + for _, h := range handlers { if x, ok := h.(TextMessageHandler); ok { - go x.HandleTextMessage(m) + if wac.shouldCallSynchronously(h) { + x.HandleTextMessage(m) + } else { + go x.HandleTextMessage(m) + } } } case ImageMessage: - for _, h := range wac.handler { + for _, h := range handlers { if x, ok := h.(ImageMessageHandler); ok { - go x.HandleImageMessage(m) + if wac.shouldCallSynchronously(h) { + x.HandleImageMessage(m) + } else { + go x.HandleImageMessage(m) + } } } case VideoMessage: - for _, h := range wac.handler { + for _, h := range handlers { if x, ok := h.(VideoMessageHandler); ok { - go x.HandleVideoMessage(m) + if wac.shouldCallSynchronously(h) { + x.HandleVideoMessage(m) + } else { + go x.HandleVideoMessage(m) + } } } case AudioMessage: - for _, h := range wac.handler { + for _, h := range handlers { if x, ok := h.(AudioMessageHandler); ok { - go x.HandleAudioMessage(m) + if wac.shouldCallSynchronously(h) { + x.HandleAudioMessage(m) + } else { + go x.HandleAudioMessage(m) + } } } case DocumentMessage: - for _, h := range wac.handler { + for _, h := range handlers { if x, ok := h.(DocumentMessageHandler); ok { - go x.HandleDocumentMessage(m) + if wac.shouldCallSynchronously(h) { + x.HandleDocumentMessage(m) + } else { + go x.HandleDocumentMessage(m) + } + } + } + case LocationMessage: + for _, h := range handlers { + if x, ok := h.(LocationMessageHandler); ok { + if wac.shouldCallSynchronously(h) { + x.HandleLocationMessage(m) + } else { + go x.HandleLocationMessage(m) + } + } + } + case LiveLocationMessage: + for _, h := range handlers { + if x, ok := h.(LiveLocationMessageHandler); ok { + if wac.shouldCallSynchronously(h) { + x.HandleLiveLocationMessage(m) + } else { + go x.HandleLiveLocationMessage(m) + } } } case *proto.WebMessageInfo: - for _, h := range wac.handler { + for _, h := range handlers { if x, ok := h.(RawMessageHandler); ok { - go x.HandleRawMessage(m) + if wac.shouldCallSynchronously(h) { + x.HandleRawMessage(m) + } else { + go x.HandleRawMessage(m) + } } } } @@ -201,7 +283,11 @@ func (wac *Conn) handleContacts(contacts interface{}) { } for _, h := range wac.handler { if x, ok := h.(ContactListHandler); ok { - go x.HandleContactList(contactList) + if wac.shouldCallSynchronously(h) { + x.HandleContactList(contactList) + } else { + go x.HandleContactList(contactList) + } } } } @@ -230,7 +316,11 @@ func (wac *Conn) handleChats(chats interface{}) { } for _, h := range wac.handler { if x, ok := h.(ChatListHandler); ok { - go x.HandleChatList(chatList) + if wac.shouldCallSynchronously(h) { + x.HandleChatList(chatList) + } else { + go x.HandleChatList(chatList) + } } } } @@ -247,7 +337,7 @@ func (wac *Conn) dispatch(msg interface{}) { for a := range con { if v, ok := con[a].(*proto.WebMessageInfo); ok { wac.handle(v) - wac.handle(parseProtoMessage(v)) + wac.handle(ParseProtoMessage(v)) } } } diff --git a/vendor/github.com/Rhymen/go-whatsapp/media.go b/vendor/github.com/Rhymen/go-whatsapp/media.go index 06d48864..1bf729bb 100644 --- a/vendor/github.com/Rhymen/go-whatsapp/media.go +++ b/vendor/github.com/Rhymen/go-whatsapp/media.go @@ -8,8 +8,6 @@ import ( "encoding/base64" "encoding/json" "fmt" - "github.com/Rhymen/go-whatsapp/crypto/cbc" - "github.com/Rhymen/go-whatsapp/crypto/hkdf" "io" "io/ioutil" "mime/multipart" @@ -17,6 +15,9 @@ import ( "os" "strings" "time" + + "github.com/Rhymen/go-whatsapp/crypto/cbc" + "github.com/Rhymen/go-whatsapp/crypto/hkdf" ) func Download(url string, mediaKey []byte, appInfo MediaType, fileLength int) ([]byte, error) { @@ -73,17 +74,18 @@ func downloadMedia(url string) (file []byte, mac []byte, err error) { return nil, nil, err } if resp.StatusCode != 200 { - return nil, nil, fmt.Errorf("download failed") + return nil, nil, fmt.Errorf("download failed with status code %d", resp.StatusCode) } defer resp.Body.Close() if resp.ContentLength <= 10 { return nil, nil, fmt.Errorf("file to short") } data, err := ioutil.ReadAll(resp.Body) - n := len(data) if err != nil { return nil, nil, err } + + n := len(data) return data[:n-10], data[n-10 : n], nil } @@ -142,7 +144,7 @@ func (wac *Conn) Upload(reader io.Reader, appInfo MediaType) (url string, mediaK select { case r := <-ch: if err = json.Unmarshal([]byte(r), &resp); err != nil { - return "", nil, nil, nil, 0, fmt.Errorf("error decoding upload response: %v\n", err) + return "", nil, nil, nil, 0, fmt.Errorf("error decoding upload response: %v", err) } case <-time.After(wac.msgTimeout): return "", nil, nil, nil, 0, fmt.Errorf("restore session init timed out") diff --git a/vendor/github.com/Rhymen/go-whatsapp/message.go b/vendor/github.com/Rhymen/go-whatsapp/message.go index 9d44fb1e..84ef15fd 100644 --- a/vendor/github.com/Rhymen/go-whatsapp/message.go +++ b/vendor/github.com/Rhymen/go-whatsapp/message.go @@ -68,6 +68,14 @@ func (wac *Conn) Send(msg interface{}) (string, error) { msgProto = getAudioProto(m) msgInfo = getMessageInfo(msgProto) ch, err = wac.sendProto(msgProto) + case LocationMessage: + msgProto = GetLocationProto(m) + msgInfo = getMessageInfo(msgProto) + ch, err = wac.sendProto(msgProto) + case LiveLocationMessage: + msgProto = GetLiveLocationProto(m) + msgInfo = getMessageInfo(msgProto) + ch, err = wac.sendProto(msgProto) default: return "ERROR", fmt.Errorf("cannot match type %T, use message types declared in the package", msg) } @@ -269,6 +277,7 @@ type VideoMessage struct { Length uint32 Type string Content io.Reader + GifPlayback bool url string mediaKey []byte fileEncSha256 []byte @@ -282,6 +291,7 @@ func getVideoMessage(msg *proto.WebMessageInfo) VideoMessage { Info: getMessageInfo(msg), Caption: vid.GetCaption(), Thumbnail: vid.GetJpegThumbnail(), + GifPlayback: vid.GetGifPlayback(), url: vid.GetUrl(), mediaKey: vid.GetMediaKey(), Length: vid.GetSeconds(), @@ -299,6 +309,7 @@ func getVideoProto(msg VideoMessage) *proto.WebMessageInfo { Caption: &msg.Caption, JpegThumbnail: msg.Thumbnail, Url: &msg.url, + GifPlayback: &msg.GifPlayback, MediaKey: msg.mediaKey, Seconds: &msg.Length, FileEncSha256: msg.fileEncSha256, @@ -431,7 +442,95 @@ func (m *DocumentMessage) Download() ([]byte, error) { return Download(m.url, m.mediaKey, MediaDocument, int(m.fileLength)) } -func parseProtoMessage(msg *proto.WebMessageInfo) interface{} { +/* +LocationMessage represents a location message +*/ +type LocationMessage struct { + Info MessageInfo + DegreesLatitude float64 + DegreesLongitude float64 + Name string + Address string + Url string + JpegThumbnail []byte +} + +func GetLocationMessage(msg *proto.WebMessageInfo) LocationMessage { + loc := msg.GetMessage().GetLocationMessage() + return LocationMessage{ + Info: getMessageInfo(msg), + DegreesLatitude: loc.GetDegreesLatitude(), + DegreesLongitude: loc.GetDegreesLongitude(), + Name: loc.GetName(), + Address: loc.GetAddress(), + Url: loc.GetUrl(), + JpegThumbnail: loc.GetJpegThumbnail(), + } +} + +func GetLocationProto(msg LocationMessage) *proto.WebMessageInfo { + p := getInfoProto(&msg.Info) + p.Message = &proto.Message{ + LocationMessage: &proto.LocationMessage{ + DegreesLatitude: &msg.DegreesLatitude, + DegreesLongitude: &msg.DegreesLongitude, + Name: &msg.Name, + Address: &msg.Address, + Url: &msg.Url, + JpegThumbnail: msg.JpegThumbnail, + }, + } + return p +} + +/* +LiveLocationMessage represents a live location message +*/ +type LiveLocationMessage struct { + Info MessageInfo + DegreesLatitude float64 + DegreesLongitude float64 + AccuracyInMeters uint32 + SpeedInMps float32 + DegreesClockwiseFromMagneticNorth uint32 + Caption string + SequenceNumber int64 + JpegThumbnail []byte +} + +func GetLiveLocationMessage(msg *proto.WebMessageInfo) LiveLocationMessage { + loc := msg.GetMessage().GetLiveLocationMessage() + return LiveLocationMessage{ + Info: getMessageInfo(msg), + DegreesLatitude: loc.GetDegreesLatitude(), + DegreesLongitude: loc.GetDegreesLongitude(), + AccuracyInMeters: loc.GetAccuracyInMeters(), + SpeedInMps: loc.GetSpeedInMps(), + DegreesClockwiseFromMagneticNorth: loc.GetDegreesClockwiseFromMagneticNorth(), + Caption: loc.GetCaption(), + SequenceNumber: loc.GetSequenceNumber(), + JpegThumbnail: loc.GetJpegThumbnail(), + } +} + +func GetLiveLocationProto(msg LiveLocationMessage) *proto.WebMessageInfo { + p := getInfoProto(&msg.Info) + p.Message = &proto.Message{ + LiveLocationMessage: &proto.LiveLocationMessage{ + DegreesLatitude: &msg.DegreesLatitude, + DegreesLongitude: &msg.DegreesLongitude, + AccuracyInMeters: &msg.AccuracyInMeters, + SpeedInMps: &msg.SpeedInMps, + DegreesClockwiseFromMagneticNorth: &msg.DegreesClockwiseFromMagneticNorth, + Caption: &msg.Caption, + SequenceNumber: &msg.SequenceNumber, + JpegThumbnail: msg.JpegThumbnail, + }, + } + return p +} + +func ParseProtoMessage(msg *proto.WebMessageInfo) interface{} { switch { case msg.GetMessage().GetAudioMessage() != nil: @@ -452,6 +551,12 @@ func parseProtoMessage(msg *proto.WebMessageInfo) interface{} { case msg.GetMessage().GetExtendedTextMessage() != nil: return getTextMessage(msg) + case msg.GetMessage().GetLocationMessage() != nil: + return GetLocationMessage(msg) + + case msg.GetMessage().GetLiveLocationMessage() != nil: + return GetLiveLocationMessage(msg) + default: //cannot match message } diff --git a/vendor/github.com/Rhymen/go-whatsapp/read.go b/vendor/github.com/Rhymen/go-whatsapp/read.go index 7281ccff..42d91ff4 100644 --- a/vendor/github.com/Rhymen/go-whatsapp/read.go +++ b/vendor/github.com/Rhymen/go-whatsapp/read.go @@ -3,6 +3,8 @@ package whatsapp import ( "crypto/hmac" "crypto/sha256" + "encoding/json" + "fmt" "github.com/Rhymen/go-whatsapp/binary" "github.com/Rhymen/go-whatsapp/crypto/cbc" "github.com/gorilla/websocket" @@ -75,7 +77,13 @@ func (wac *Conn) processReadData(msgType int, msg []byte) error { wac.listener.Lock() delete(wac.listener.m, data[0]) wac.listener.Unlock() - } else if msgType == websocket.BinaryMessage && wac.loggedIn { + } else if msgType == websocket.BinaryMessage { + wac.loginSessionLock.RLock() + sess := wac.session + wac.loginSessionLock.RUnlock() + if sess == nil || sess.MacKey == nil || sess.EncKey == nil { + return ErrInvalidWsState + } message, err := wac.decryptBinaryMessage([]byte(data[1])) if err != nil { return errors.Wrap(err, "error decoding binary") @@ -90,6 +98,21 @@ func (wac *Conn) processReadData(msgType int, msg []byte) error { func (wac *Conn) decryptBinaryMessage(msg []byte) (*binary.Node, error) { //message validation h2 := hmac.New(sha256.New, wac.session.MacKey) + if len(msg) < 33 { + var response struct { + Status int `json:"status"` + } + err := json.Unmarshal(msg, &response) + if err == nil { + if response.Status == 404 { + return nil, ErrServerRespondedWith404 + } + return nil, errors.New(fmt.Sprintf("server responded with %d", response.Status)) + } else { + return nil, ErrInvalidServerResponse + } + + } h2.Write([]byte(msg[32:])) if !hmac.Equal(h2.Sum(nil), msg[:32]) { return nil, ErrInvalidHmac diff --git a/vendor/github.com/Rhymen/go-whatsapp/session.go b/vendor/github.com/Rhymen/go-whatsapp/session.go index 3b9ba235..11307fab 100644 --- a/vendor/github.com/Rhymen/go-whatsapp/session.go +++ b/vendor/github.com/Rhymen/go-whatsapp/session.go @@ -100,6 +100,14 @@ func (wac *Conn) SetClientName(long, short string) error { return nil } +/* +SetClientVersion sets WhatsApp client version +Default value is 0.3.3324 +*/ +func (wac *Conn) SetClientVersion(major int, minor int, patch int) { + waVersion = []int{major, minor, patch} +} + /* Login is the function that creates a new whatsapp session and logs you in. If you do not want to scan the qr code every time, you should save the returned session and use RestoreWithSession the next time. Login takes a writable channel @@ -187,6 +195,8 @@ func (wac *Conn) Login(qrChan chan<- string) (Session, error) { var resp2 []interface{} select { case r1 := <-s1: + wac.loginSessionLock.Lock() + defer wac.loginSessionLock.Unlock() if err := json.Unmarshal([]byte(r1), &resp2); err != nil { return session, fmt.Errorf("error decoding qr code resp: %v", err) } diff --git a/vendor/github.com/Rhymen/go-whatsapp/write.go b/vendor/github.com/Rhymen/go-whatsapp/write.go index 4ea63dca..2b07ca6b 100644 --- a/vendor/github.com/Rhymen/go-whatsapp/write.go +++ b/vendor/github.com/Rhymen/go-whatsapp/write.go @@ -78,6 +78,36 @@ func (wac *Conn) sendKeepAlive() error { return nil } +/* + When phone is unreachable, WhatsAppWeb sends ["admin","test"] time after time to try a successful contact. + Tested with Airplane mode and no connection at all. +*/ +func (wac *Conn) sendAdminTest() (bool, error) { + data := []interface{}{"admin", "test"} + + r, err := wac.writeJson(data) + if err != nil { + return false, errors.Wrap(err, "error sending admin test") + } + + var response []interface{} + + select { + case resp := <-r: + if err := json.Unmarshal([]byte(resp), &response); err != nil { + return false, fmt.Errorf("error decoding response message: %v\n", err) + } + case <-time.After(wac.msgTimeout): + return false, ErrConnectionTimeout + } + + if len(response) == 2 && response[0].(string) == "Pong" && response[1].(bool) == true { + return true, nil + } else{ + return false, nil + } +} + func (wac *Conn) write(messageType int, answerMessageTag string, data []byte) (<-chan string, error) { var ch chan string if answerMessageTag != "" { -- cgit v1.2.3