conn.go 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  1. //Package whatsapp provides a developer API to interact with the WhatsAppWeb-Servers.
  2. package whatsapp
  3. import (
  4. "crypto/hmac"
  5. "crypto/sha256"
  6. "encoding/json"
  7. "fmt"
  8. "github.com/Rhymen/go-whatsapp/binary"
  9. "github.com/Rhymen/go-whatsapp/crypto/cbc"
  10. "github.com/gorilla/websocket"
  11. "math/rand"
  12. "net/http"
  13. "os"
  14. "strconv"
  15. "strings"
  16. "sync"
  17. "time"
  18. )
  19. type metric byte
  20. const (
  21. debugLog metric = iota + 1
  22. queryResume
  23. queryReceipt
  24. queryMedia
  25. queryChat
  26. queryContacts
  27. queryMessages
  28. presence
  29. presenceSubscribe
  30. group
  31. read
  32. chat
  33. received
  34. pic
  35. status
  36. message
  37. queryActions
  38. block
  39. queryGroup
  40. queryPreview
  41. queryEmoji
  42. queryMessageInfo
  43. spam
  44. querySearch
  45. queryIdentity
  46. queryUrl
  47. profile
  48. contact
  49. queryVcard
  50. queryStatus
  51. queryStatusUpdate
  52. privacyStatus
  53. queryLiveLocations
  54. liveLocation
  55. queryVname
  56. queryLabels
  57. call
  58. queryCall
  59. queryQuickReplies
  60. )
  61. type flag byte
  62. const (
  63. ignore flag = 1 << (7 - iota)
  64. ackRequest
  65. available
  66. notAvailable
  67. expires
  68. skipOffline
  69. )
  70. /*
  71. Conn is created by NewConn. Interacting with the initialized Conn is the main way of interacting with our package.
  72. It holds all necessary information to make the package work internally.
  73. */
  74. type Conn struct {
  75. wsConn *websocket.Conn
  76. session *Session
  77. listener map[string]chan string
  78. listenerMutex sync.RWMutex
  79. writeChan chan wsMsg
  80. handler []Handler
  81. msgCount int
  82. msgTimeout time.Duration
  83. Info *Info
  84. Store *Store
  85. ServerLastSeen time.Time
  86. }
  87. type wsMsg struct {
  88. messageType int
  89. data []byte
  90. }
  91. /*
  92. Creates a new connection with a given timeout. The websocket connection to the WhatsAppWeb servers get´s established.
  93. The goroutine for handling incoming messages is started
  94. */
  95. func NewConn(timeout time.Duration) (*Conn, error) {
  96. dialer := &websocket.Dialer{
  97. ReadBufferSize: 25 * 1024 * 1024,
  98. WriteBufferSize: 10 * 1024 * 1024,
  99. HandshakeTimeout: timeout,
  100. }
  101. headers := http.Header{"Origin": []string{"https://web.whatsapp.com"}}
  102. wsConn, _, err := dialer.Dial("wss://w3.web.whatsapp.com/ws", headers)
  103. if err != nil {
  104. return nil, fmt.Errorf("couldn't dial whatsapp web websocket: %v", err)
  105. }
  106. wac := &Conn{
  107. wsConn: wsConn,
  108. listener: make(map[string]chan string),
  109. listenerMutex: sync.RWMutex{},
  110. writeChan: make(chan wsMsg),
  111. handler: make([]Handler, 0),
  112. msgCount: 0,
  113. msgTimeout: timeout,
  114. Store: newStore(),
  115. }
  116. go wac.readPump()
  117. go wac.writePump()
  118. go wac.keepAlive(20000, 90000)
  119. return wac, nil
  120. }
  121. func (wac *Conn) write(data []interface{}) (<-chan string, error) {
  122. d, err := json.Marshal(data)
  123. if err != nil {
  124. return nil, err
  125. }
  126. ts := time.Now().Unix()
  127. messageTag := fmt.Sprintf("%d.--%d", ts, wac.msgCount)
  128. msg := fmt.Sprintf("%s,%s", messageTag, d)
  129. ch := make(chan string, 1)
  130. wac.listenerMutex.Lock()
  131. wac.listener[messageTag] = ch
  132. wac.listenerMutex.Unlock()
  133. wac.writeChan <- wsMsg{websocket.TextMessage, []byte(msg)}
  134. wac.msgCount++
  135. return ch, nil
  136. }
  137. func (wac *Conn) writeBinary(node binary.Node, metric metric, flag flag, tag string) (<-chan string, error) {
  138. if len(tag) < 2 {
  139. return nil, fmt.Errorf("no tag specified or to short")
  140. }
  141. b, err := binary.Marshal(node)
  142. if err != nil {
  143. return nil, err
  144. }
  145. cipher, err := cbc.Encrypt(wac.session.EncKey, nil, b)
  146. if err != nil {
  147. return nil, err
  148. }
  149. h := hmac.New(sha256.New, wac.session.MacKey)
  150. h.Write(cipher)
  151. hash := h.Sum(nil)
  152. data := []byte(tag + ",")
  153. data = append(data, byte(metric), byte(flag))
  154. data = append(data, hash[:32]...)
  155. data = append(data, cipher...)
  156. ch := make(chan string, 1)
  157. wac.listenerMutex.Lock()
  158. wac.listener[tag] = ch
  159. wac.listenerMutex.Unlock()
  160. msg := wsMsg{websocket.BinaryMessage, data}
  161. wac.writeChan <- msg
  162. wac.msgCount++
  163. return ch, nil
  164. }
  165. func (wac *Conn) readPump() {
  166. defer wac.wsConn.Close()
  167. for {
  168. msgType, msg, err := wac.wsConn.ReadMessage()
  169. if err != nil {
  170. if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway) {
  171. wac.handle(fmt.Errorf("unexpected websocket close: %v", err))
  172. }
  173. break
  174. }
  175. data := strings.SplitN(string(msg), ",", 2)
  176. //Kepp-Alive Timestmap
  177. if data[0][0] == '!' {
  178. msecs, err := strconv.ParseInt(data[0][1:], 10, 64)
  179. if err != nil {
  180. fmt.Fprintf(os.Stderr, "Error converting time string to uint: %v\n", err)
  181. continue
  182. }
  183. wac.ServerLastSeen = time.Unix(msecs/1000, (msecs%1000)*int64(time.Millisecond))
  184. continue
  185. }
  186. wac.listenerMutex.RLock()
  187. listener, hasListener := wac.listener[data[0]]
  188. wac.listenerMutex.RUnlock()
  189. if hasListener && len(data[1]) > 0 {
  190. listener <- data[1]
  191. wac.listenerMutex.Lock()
  192. delete(wac.listener, data[0])
  193. wac.listenerMutex.Unlock()
  194. } else if msgType == 2 && wac.session != nil && wac.session.EncKey != nil {
  195. message, err := wac.decryptBinaryMessage([]byte(data[1]))
  196. if err != nil {
  197. wac.handle(fmt.Errorf("error decoding binary: %v", err))
  198. continue
  199. }
  200. wac.dispatch(message)
  201. } else {
  202. if len(data[1]) > 0 {
  203. wac.handle(string(data[1]))
  204. }
  205. }
  206. }
  207. }
  208. func (wac *Conn) writePump() {
  209. for msg := range wac.writeChan {
  210. if err := wac.wsConn.WriteMessage(msg.messageType, msg.data); err != nil {
  211. fmt.Fprintf(os.Stderr, "error writing to socket: %v", err)
  212. }
  213. }
  214. }
  215. func (wac *Conn) keepAlive(minIntervalMs int, maxIntervalMs int) {
  216. for {
  217. wac.writeChan <- wsMsg{
  218. messageType: websocket.TextMessage,
  219. data: []byte("?,,"),
  220. }
  221. interval := rand.Intn(maxIntervalMs-minIntervalMs) + minIntervalMs
  222. <-time.After(time.Duration(interval) * time.Millisecond)
  223. }
  224. }
  225. func (wac *Conn) decryptBinaryMessage(msg []byte) (*binary.Node, error) {
  226. //message validation
  227. h2 := hmac.New(sha256.New, wac.session.MacKey)
  228. h2.Write([]byte(msg[32:]))
  229. if !hmac.Equal(h2.Sum(nil), msg[:32]) {
  230. return nil, fmt.Errorf("message received with invalid hmac")
  231. }
  232. // message decrypt
  233. d, err := cbc.Decrypt(wac.session.EncKey, nil, msg[32:])
  234. if err != nil {
  235. return nil, fmt.Errorf("error decrypting message with AES: %v", err)
  236. }
  237. // message unmarshal
  238. message, err := binary.Unmarshal(d)
  239. if err != nil {
  240. return nil, fmt.Errorf("error decoding binary: %v", err)
  241. }
  242. return message, nil
  243. }