PageRenderTime 62ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 1ms

/src/php/whatsprot.class.php

https://github.com/joec921/WhatsAPI
PHP | 1377 lines | 861 code | 127 blank | 389 comment | 135 complexity | f6ac64d74ebc648b39d99d7c43e1c7dd MD5 | raw file
  1. <?php
  2. require 'protocol.class.php';
  3. require 'WhatsAppEvent.php';
  4. require 'func.php';
  5. require 'rc4.php';
  6. class WhatsProt
  7. {
  8. /**
  9. * Constant declarations.
  10. */
  11. // The hostname of the whatsapp server.
  12. const _whatsAppHost = 'c.whatsapp.net';
  13. // The hostnames used to login/send messages.
  14. const _whatsAppServer = 's.whatsapp.net';
  15. const _whatsAppGroupServer = 'g.us';
  16. // The device name.
  17. const _device = 'iPhone';
  18. // The WhatsApp version.
  19. const _whatsAppVer = '2.8.7';
  20. // The port of the whatsapp server.
  21. const _port = 5222;
  22. // The timeout for the connection with the Whatsapp servers.
  23. const _timeoutSec = 2;
  24. const _timeoutUsec = 0;
  25. // The request code host.
  26. const _whatsAppReqHost = 'v.whatsapp.net/v2/code';
  27. // The register code host.
  28. const _whatsAppRegHost = 'v.whatsapp.net/v2/register';
  29. // The check credentials host.
  30. const _whatsAppCheHost = 'v.whatsapp.net/v2/exist';
  31. // User agent and token used in reques/registration code.
  32. const _whatsAppUserAgent = 'WhatsApp/2.3.53 S40Version/14.26 Device/Nokia302';
  33. const _whatsAppToken = 'PdA2DJyKoUrwLw1Bg6EIhzh502dF9noR9uFCllGk1354754753509';
  34. // The upload host.
  35. const _whatsAppUploadHost = 'https://mms.whatsapp.net/client/iphone/upload.php';
  36. // Describes the connection status with the whatsapp server.
  37. const _disconnectedStatus = 'disconnected';
  38. const _connectedStatus = 'connected';
  39. /**
  40. * Property declarations.
  41. */
  42. // The user phone number including the country code without '+' or '00'.
  43. protected $_phoneNumber;
  44. // The IMEI/MAC adress.
  45. protected $_identity;
  46. // The user password.
  47. protected $_password;
  48. // The user name.
  49. protected $_name;
  50. // A list of bytes for incomplete messages.
  51. protected $_incomplete_message = '';
  52. // Holds the login status.
  53. protected $_loginStatus;
  54. // The AccountInfo object.
  55. protected $_accountinfo;
  56. // Queue for received messages.
  57. protected $_messageQueue = array();
  58. // Queue for outgoing messages.
  59. protected $_outQueue = array();
  60. // Id to the last message sent.
  61. protected $_lastId = FALSE;
  62. // Id to the last grouip id created.
  63. protected $_lastGroupId = FALSE;
  64. // Message counter for auto-id.
  65. protected $_msgCounter = 1;
  66. // A socket to connect to the whatsapp network.
  67. protected $_socket;
  68. // An instance of the BinaryTreeNodeWriter class.
  69. protected $_writer;
  70. // An instance of the BinaryTreeNodeReader class.
  71. protected $_reader;
  72. // An instance of the WhatsAppEvent class.
  73. protected $event;
  74. // Instances of the KeyStream class.
  75. protected $_inputKey;
  76. protected $_outputKey;
  77. // Determines wether debug mode is on or off.
  78. protected $_debug;
  79. protected $_newmsgBind = FALSE;
  80. /**
  81. * Default class constructor.
  82. *
  83. * @param $Number
  84. * The user phone number including the country code without '+' or '00'.
  85. * @param $imei
  86. * The IMEI/MAC adress.
  87. * @param $Nickname
  88. * The user name.
  89. * @param $debug
  90. * Debug on or off, false by default.
  91. */
  92. public function __construct($Number, $identity, $Nickname, $debug = FALSE)
  93. {
  94. $this->_debug = $debug;
  95. $dict = getDictionary();
  96. $this->_writer = new BinTreeNodeWriter($dict);
  97. $this->_reader = new BinTreeNodeReader($dict);
  98. $this->_phoneNumber = $Number;
  99. $this->_identity = $identity;
  100. $this->_name = $Nickname;
  101. $this->_loginStatus = WhatsProt::_disconnectedStatus;
  102. }
  103. /**
  104. * Add stream features.
  105. *
  106. * @return ProtocolNode
  107. * Return itself.
  108. */
  109. protected function addFeatures()
  110. {
  111. $child = new ProtocolNode("receipt_acks", NULL, NULL, "");
  112. $parent = new ProtocolNode("stream:features", NULL, array($child), "");
  113. return $parent;
  114. }
  115. /**
  116. * Add the authenication nodes.
  117. *
  118. * @return ProtocolNode
  119. * Return itself.
  120. */
  121. protected function addAuth()
  122. {
  123. $authHash = array();
  124. $authHash["xmlns"] = "urn:ietf:params:xml:ns:xmpp-sasl";
  125. $authHash["mechanism"] = "WAUTH-1";
  126. $authHash["user"] = $this->_phoneNumber;
  127. $node = new ProtocolNode("auth", $authHash, NULL, "");
  128. return $node;
  129. }
  130. /**
  131. * Encrypt the password.
  132. *
  133. * @return string
  134. * Return the encrypt password.
  135. */
  136. public function encryptPassword()
  137. {
  138. return base64_decode($this->_password);
  139. }
  140. protected function authenticate()
  141. {
  142. $key = pbkdf2('sha1', $this->encryptPassword(), $this->challengeData, 16, 20, TRUE);
  143. $this->_inputKey = new KeyStream($key);
  144. $this->_outputKey = new KeyStream($key);
  145. $array = $this->_phoneNumber . $this->challengeData . time();
  146. $response = $this->_outputKey->encode($array, 0, strlen($array), FALSE);
  147. return $response;
  148. }
  149. /**
  150. * Sets the bind of th new message.
  151. */
  152. public function setNewMessageBind($bind)
  153. {
  154. $this->_newmsgBind = $bind;
  155. }
  156. /**
  157. * Add message to the outgoing queue.
  158. */
  159. public function addOutQueue($node)
  160. {
  161. $this->_outQueue[] = $node;
  162. }
  163. /**
  164. * Add the auth response to protocoltreenode.
  165. *
  166. * @return ProtocolNode
  167. * Return itself.
  168. */
  169. protected function addAuthResponse()
  170. {
  171. $resp = $this->authenticate();
  172. $respHash = array();
  173. $respHash["xmlns"] = "urn:ietf:params:xml:ns:xmpp-sasl";
  174. $node = new ProtocolNode("response", $respHash, NULL, $resp);
  175. return $node;
  176. }
  177. /**
  178. * Send data to the whatsapp server.
  179. */
  180. protected function sendData($data)
  181. {
  182. socket_send($this->_socket, $data, strlen($data), 0);
  183. }
  184. /**
  185. * Send node to the whatsapp server.
  186. */
  187. protected function sendNode($node)
  188. {
  189. $this->DebugPrint($node->NodeString("tx ") . "\n");
  190. $this->sendData($this->_writer->write($node));
  191. }
  192. /**
  193. * Send node to the servers.
  194. *
  195. * @param $to
  196. * The reciepient to send.
  197. * @param $node
  198. * The node that contains the message.
  199. */
  200. protected function SendMessageNode($to, $node)
  201. {
  202. $serverNode = new ProtocolNode("server", NULL, NULL, "");
  203. $xHash = array();
  204. $xHash["xmlns"] = "jabber:x:event";
  205. $xNode = new ProtocolNode("x", $xHash, array($serverNode), "");
  206. $notify = array();
  207. $notify['xmlns'] = 'urn:xmpp:whatsapp';
  208. $notify['name'] = $this->_name;
  209. $notnode = new ProtocolNode("notify", $notify, NULL, "");
  210. $request = array();
  211. $request['xmlns'] = "urn:xmpp:receipts";
  212. $reqnode = new ProtocolNode("request", $request, NULL, "");
  213. $whatsAppServer = WhatsProt::_whatsAppServer;
  214. if (strpos($to, "-") !== FALSE) {
  215. $whatsAppServer = WhatsProt::_whatsAppGroupServer;
  216. }
  217. $messageHash = array();
  218. $messageHash["to"] = $to . "@" . $whatsAppServer;
  219. $messageHash["type"] = "chat";
  220. $messageHash["id"] = $this->msgId();
  221. $messageHash["t"] = time();
  222. $messsageNode = new ProtocolNode("message", $messageHash, array($xNode, $notnode, $reqnode, $node), "");
  223. if (!$this->_lastId) {
  224. $this->_lastId = $messageHash["id"];
  225. $this->sendNode($messsageNode);
  226. } else {
  227. $this->_outQueue[] = $messsageNode;
  228. }
  229. }
  230. /**
  231. * Read 1024 bytes from the whatsapp server.
  232. */
  233. protected function readData()
  234. {
  235. $buff = '';
  236. $ret = socket_read($this->_socket, 1024);
  237. if ($ret) {
  238. $buff = $this->_incomplete_message . $ret;
  239. $this->_incomplete_message = '';
  240. } else {
  241. $error = socket_strerror(socket_last_error($this->_socket));
  242. $this->eventManager()->fire('onClose', array($this->_phoneNumber, $error));
  243. }
  244. return $buff;
  245. }
  246. /**
  247. * Process the challenge.
  248. *
  249. * @param $node
  250. * The node that contains the challenge.
  251. */
  252. protected function processChallenge($node)
  253. {
  254. $this->challengeData = $node->_data;
  255. }
  256. /**
  257. * Tell the server we received the message.
  258. *
  259. * @param $msg
  260. * The ProtocolTreeNode that contains the message.
  261. */
  262. protected function sendMessageReceived($msg)
  263. {
  264. $requestNode = $msg->getChild("request");
  265. $receivedNode = $msg->getChild("received");
  266. if ($requestNode != NULL || $receivedNode != NULL) {
  267. $recievedHash = array();
  268. $recievedHash["xmlns"] = "urn:xmpp:receipts";
  269. $receivedNode = new ProtocolNode("received", $recievedHash, NULL, "");
  270. $messageHash = array();
  271. $messageHash["to"] = $msg->getAttribute("from");
  272. $messageHash["type"] = "chat";
  273. $messageHash["id"] = $msg->getAttribute("id");
  274. $messageHash["t"] = time();
  275. $messageNode = new ProtocolNode("message", $messageHash, array($receivedNode), "");
  276. $this->sendNode($messageNode);
  277. $this->eventManager()->fire('onSendMessageReceived', array($this->_phoneNumber, $messageHash["t"], $msg->getAttribute("from")));
  278. }
  279. }
  280. /**
  281. * Process inbound data.
  282. *
  283. * @param $Data
  284. * The data to process.
  285. */
  286. protected function processInboundData($data)
  287. {
  288. try {
  289. $node = $this->_reader->nextTree($data);
  290. while ($node != NULL) {
  291. $this->DebugPrint($node->NodeString("rx ") . "\n");
  292. if (strcmp($node->_tag, "challenge") == 0) {
  293. $this->processChallenge($node);
  294. } elseif (strcmp($node->_tag, "success") == 0) {
  295. $this->_loginStatus = WhatsProt::_connectedStatus;
  296. }
  297. if (strcmp($node->_tag, "message") == 0) {
  298. array_push($this->_messageQueue, $node);
  299. $this->sendMessageReceived($node);
  300. if ($node->hasChild('x') && $this->_lastId == $node->getAttribute('id')) {
  301. $this->sendNext();
  302. }
  303. if ($this->_newmsgBind && $node->getChild('body')) {
  304. $this->_newmsgBind->process($node);
  305. }
  306. if ($node->getChild('composing') != NULL) {
  307. $this->eventManager()->fire('onUserComposing', array(
  308. $this->_phoneNumber,
  309. $node->_attributeHash['from'], $node->_attributeHash['id'], $node->_attributeHash['type'], $node->_attributeHash['t']
  310. ));
  311. }
  312. if ($node->getChild('paused') != NULL) {
  313. $this->eventManager()->fire('onUserPaused', array(
  314. $this->_phoneNumber,
  315. $node->_attributeHash['from'],
  316. $node->_attributeHash['id'],
  317. $node->_attributeHash['type'],
  318. $node->_attributeHash['t']
  319. ));
  320. }
  321. if ($node->getChild('notify') != NULL && $node->_children[0]->getAttribute('name') != '' && $node->getChild('body') != NULL) {
  322. $this->eventManager()->fire('onGetMessage', array(
  323. $this->_phoneNumber,
  324. $node->_attributeHash['from'], $node->_attributeHash['id'], $node->_attributeHash['type'], $node->_attributeHash['t'],
  325. $node->_children[0]->getAttribute('name'),
  326. $node->_children[2]->_data
  327. ));
  328. }
  329. if ($node->getChild('notify') != NULL && $node->_children[0]->getAttribute('name') != '' && $node->getChild('media') != NULL) {
  330. if ($node->_children[2]->getAttribute('type') == 'image') {
  331. $this->eventManager()->fire('onGetImage', array(
  332. $this->_phoneNumber,
  333. $node->_attributeHash['from'], $node->_attributeHash['id'], $node->_attributeHash['type'], $node->_attributeHash['t'],
  334. $node->_children[0]->getAttribute('name'),
  335. $node->_children[2]->getAttribute('size'),
  336. $node->_children[2]->getAttribute('url'),
  337. $node->_children[2]->getAttribute('file'),
  338. $node->_children[2]->getAttribute('mimetype'),
  339. $node->_children[2]->getAttribute('filehash'),
  340. $node->_children[2]->getAttribute('width'),
  341. $node->_children[2]->getAttribute('height'),
  342. $node->_children[2]->_data
  343. ));
  344. } elseif ($node->_children[2]->getAttribute('type') == 'video') {
  345. $this->eventManager()->fire('onGetVideo', array(
  346. $this->_phoneNumber,
  347. $node->_attributeHash['from'], $node->_attributeHash['id'], $node->_attributeHash['type'], $node->_attributeHash['t'],
  348. $node->_children[0]->getAttribute('name'),
  349. $node->_children[2]->getAttribute('url'),
  350. $node->_children[2]->getAttribute('file'),
  351. $node->_children[2]->getAttribute('size'),
  352. $node->_children[2]->getAttribute('mimetype'),
  353. $node->_children[2]->getAttribute('filehash'),
  354. $node->_children[2]->getAttribute('duration'),
  355. $node->_children[2]->getAttribute('vcodec'),
  356. $node->_children[2]->getAttribute('acodec'),
  357. $node->_children[2]->_data
  358. ));
  359. } elseif ($node->_children[2]->getAttribute('type') == 'audio') {
  360. $this->eventManager()->fire('onGetAudio', array(
  361. $this->_phoneNumber,
  362. $node->_attributeHash['from'], $node->_attributeHash['id'], $node->_attributeHash['type'], $node->_attributeHash['t'],
  363. $node->_children[0]->getAttribute('name'),
  364. $node->_children[2]->getAttribute('size'),
  365. $node->_children[2]->getAttribute('url'),
  366. $node->_children[2]->getAttribute('file'),
  367. $node->_children[2]->getAttribute('mimetype'),
  368. $node->_children[2]->getAttribute('filehash'),
  369. $node->_children[2]->getAttribute('duration'),
  370. $node->_children[2]->getAttribute('acodec'),
  371. ));
  372. } elseif ($node->_children[2]->getAttribute('type') == 'vcard') {
  373. $this->eventManager()->fire('onGetvCard', array(
  374. $this->_phoneNumber,
  375. $node->_attributeHash['from'], $node->_attributeHash['id'], $node->_attributeHash['type'], $node->_attributeHash['t'],
  376. $node->_children[0]->getAttribute('name'),
  377. $node->_children[2]->_children[0]->getAttribute('name'),
  378. $node->_children[2]->_children[0]->_data
  379. ));
  380. } elseif ($node->_children[2]->getAttribute('type') == 'location' && !isset($node->_children[2]->_attributeHash['url'])) {
  381. $this->eventManager()->fire('onGetLocation', array(
  382. $this->_phoneNumber,
  383. $node->_attributeHash['from'], $node->_attributeHash['id'], $node->_attributeHash['type'], $node->_attributeHash['t'],
  384. $node->_children[0]->getAttribute('name'),
  385. $node->_children[2]->getAttribute('longitude'),
  386. $node->_children[2]->getAttribute('latitude'),
  387. $node->_children[2]->_data
  388. ));
  389. } elseif ($node->_children[2]->getAttribute('type') == 'location' && isset($node->_children[2]->_attributeHash['url'])) {
  390. $this->eventManager()->fire('onGetPlace', array(
  391. $this->_phoneNumber,
  392. $node->_attributeHash['from'], $node->_attributeHash['id'], $node->_attributeHash['type'], $node->_attributeHash['t'],
  393. $node->_children[0]->getAttribute('name'),
  394. $node->_children[2]->getAttribute('name'),
  395. $node->_children[2]->getAttribute('longitude'),
  396. $node->_children[2]->getAttribute('latitude'),
  397. $node->_children[2]->getAttribute('url'),
  398. $node->_children[2]->_data
  399. ));
  400. }
  401. }
  402. if ($node->getChild('x') != NULL) {
  403. $this->eventManager()->fire('onMessageReceivedServer', array(
  404. $this->_phoneNumber,
  405. $node->_attributeHash['from'], $node->_attributeHash['id'], $node->_attributeHash['type'], $node->_attributeHash['t']
  406. ));
  407. }
  408. if ($node->getChild('received') != NULL) {
  409. $this->eventManager()->fire('onMessageReceivedClient', array(
  410. $this->_phoneNumber,
  411. $node->_attributeHash['from'], $node->_attributeHash['id'], $node->_attributeHash['type'], $node->_attributeHash['t']
  412. ));
  413. }
  414. if (strcmp($node->_attributeHash['type'], "subject") == 0) {print_r($node);
  415. $this->eventManager()->fire('onGetGroupSubject', array(
  416. $this->_phoneNumber,
  417. reset(explode('@', $node->_attributeHash['from'])), $node->_attributeHash['t'], reset(explode('@', $node->_attributeHash['author'])),
  418. $node->_children[0]->getAttribute('name'),
  419. $node->_children[2]->_data,
  420. ));
  421. }
  422. }
  423. if (strcmp($node->_tag, "presence") == 0 && strncmp($node->_attributeHash['from'], $this->_phoneNumber, strlen($this->_phoneNumber)) != 0 && strpos($node->_attributeHash['from'], "-") !== FALSE && isset($node->_attributeHash['type'])) {
  424. $this->eventManager()->fire('onGetPresence', array(
  425. $this->_phoneNumber,
  426. $node->_attributeHash['from'], $node->_attributeHash['type']
  427. ));
  428. }
  429. if (strcmp($node->_tag, "presence") == 0 && strncmp($node->_attributeHash['from'], $this->_phoneNumber, strlen($this->_phoneNumber)) != 0 && strpos($node->_attributeHash['from'], "-") !== FALSE && isset($node->_attributeHash['type'])) {
  430. $groupId = reset(explode('@', $node->_attributeHash['from']));
  431. if (isset($node->_attributeHash['add'])) {
  432. $this->eventManager()->fire('onAddParticipantGroup', array(
  433. $this->_phoneNumber,
  434. $groupId, reset(explode('@', $node->_attributeHash['add']))
  435. ));
  436. } elseif (isset($node->_attributeHash['remove'])) {
  437. $this->eventManager()->fire('onRemoveParticipantGroup', array(
  438. $this->_phoneNumber,
  439. $groupId, reset(explode('@', $node->_attributeHash['remove'])), reset(explode('@', $node->_attributeHash['author']))
  440. ));
  441. }
  442. }
  443. if (strcmp($node->_tag, "iq") == 0 && strcmp($node->_attributeHash['type'], "get") == 0 && strcmp($node->_children[0]->_tag, "ping") == 0) {
  444. $this->eventManager()->fire('onPing', array($this->_phoneNumber, $node->_attributeHash['id']));
  445. $this->Pong($node->_attributeHash['id']);
  446. }
  447. if (strcmp($node->_tag, "iq") == 0 && strcmp($node->_attributeHash['type'], "result") == 0 && strcmp($node->_children[0]->_tag, "query") == 0) {
  448. array_push($this->_messageQueue, $node);
  449. }
  450. if (strcmp($node->_tag, "iq") == 0 && strcmp($node->_attributeHash['type'], "result") == 0 && strcmp($node->_children[0]->_tag, "group") == 0) {
  451. $this->_lastGroupId = $node->_children[0]->_attributeHash['id'];
  452. $this->eventManager()->fire('onCreateGroupChat', array(
  453. $this->_phoneNumber,
  454. $node->_children[0]->_attributeHash['id']
  455. ));
  456. }
  457. $node = $this->_reader->nextTree();
  458. }
  459. } catch (IncompleteMessageException $e) {
  460. $this->_incomplete_message = $e->getInput();
  461. }
  462. }
  463. /**
  464. * Send the next message.
  465. */
  466. public function sendNext()
  467. {
  468. if (count($this->_outQueue) > 0) {
  469. $msgnode = array_shift($this->_outQueue);
  470. $msgnode->refreshTimes();
  471. $this->_lastId = $msgnode->getAttribute('id');
  472. $this->sendNode($msgnode);
  473. } else {
  474. $this->_lastId = FALSE;
  475. }
  476. }
  477. /**
  478. * Connect to the WhatsApp network.
  479. */
  480. public function Connect()
  481. {
  482. $Socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
  483. socket_connect($Socket, WhatsProt::_whatsAppHost, WhatsProt::_port);
  484. $this->_socket = $Socket;
  485. socket_set_option($this->_socket, SOL_SOCKET, SO_RCVTIMEO, array('sec' => WhatsProt::_timeoutSec, 'usec' => WhatsProt::_timeoutUsec));
  486. $this->eventManager()->fire('onConnect', array($this->_phoneNumber, $this->_socket));
  487. }
  488. /**
  489. * Disconnect to the WhatsApp network.
  490. */
  491. public function Disconnect()
  492. {
  493. socket_close($this->_socket);
  494. $this->eventManager()->fire('onDisconnect', array($this->_phoneNumber, $this->_socket));
  495. }
  496. /**
  497. * Logs us in to the server.
  498. */
  499. public function Login()
  500. {
  501. $this->_accountinfo = (array) $this->checkCredentials();
  502. if ($this->_accountinfo['status'] == 'ok') {
  503. $this->_password = $this->_accountinfo['pw'];
  504. }
  505. $resource = WhatsProt::_device . '-' . WhatsProt::_whatsAppVer . '-' . WhatsProt::_port;
  506. $data = $this->_writer->StartStream(WhatsProt::_whatsAppServer, $resource);
  507. $feat = $this->addFeatures();
  508. $auth = $this->addAuth();
  509. $this->sendData($data);
  510. $this->sendNode($feat);
  511. $this->sendNode($auth);
  512. $this->processInboundData($this->readData());
  513. $data = $this->addAuthResponse();
  514. $this->sendNode($data);
  515. $this->_reader->setKey($this->_inputKey);
  516. $this->_writer->setKey($this->_outputKey);
  517. $cnt = 0;
  518. do {
  519. $this->processInboundData($this->readData());
  520. } while (($cnt++ < 100) && (strcmp($this->_loginStatus, WhatsProt::_disconnectedStatus) == 0));
  521. $this->eventManager()->fire('onLogin', array($this->_phoneNumber));
  522. $this->SendPresence();
  523. }
  524. /**
  525. * Pull from the socket, and place incoming messages in the message queue.
  526. */
  527. public function PollMessages()
  528. {
  529. $this->processInboundData($this->readData());
  530. }
  531. /**
  532. * Drain the message queue for application processing.
  533. *
  534. * @return array
  535. * Return the message queue list.
  536. */
  537. public function GetMessages()
  538. {
  539. $ret = $this->_messageQueue;
  540. $this->_messageQueue = array();
  541. return $ret;
  542. }
  543. /**
  544. * Wait for message delivery notification.
  545. */
  546. public function WaitforReceipt()
  547. {
  548. $received = FALSE;
  549. do {
  550. $this->PollMessages();
  551. $msgs = $this->GetMessages();
  552. foreach ($msgs as $m) {
  553. // Process inbound messages.
  554. if ($m->_tag == "message") {
  555. if ($m->getChild('received') != NULL && !isset($m->_attributeHas['retry'])) {
  556. $received = TRUE;
  557. } elseif ($m->getChild('received') != NULL && isset($m->_attributeHas['retry'])) {
  558. throw new Exception('There was a problem trying to send the message, please retry.');
  559. }
  560. }
  561. //print($m->NodeString("") . "\n");
  562. }
  563. } while (!$received);
  564. }
  565. /**
  566. * Wait for group notification.
  567. */
  568. public function WaitforGroupId()
  569. {
  570. $this->_lastGroupId = FALSE;
  571. do {
  572. $this->PollMessages();
  573. $msgs = $this->GetMessages();
  574. } while (!$this->_lastGroupId);
  575. }
  576. /**
  577. * Send presence status.
  578. *
  579. * @param $type
  580. * The presence status.
  581. */
  582. public function SendPresence($type = "available")
  583. {
  584. $presence = array();
  585. $presence['type'] = $type;
  586. $presence['name'] = $this->_name;
  587. $node = new ProtocolNode("presence", $presence, NULL, "");
  588. $this->sendNode($node);
  589. $this->eventManager()->fire('onSendPresence', array($this->_phoneNumber, $presence['type'], $presence['name']));
  590. }
  591. /**
  592. * Update de user status.
  593. *
  594. * @param $text
  595. * The text message status to send.
  596. */
  597. public function sendStatusUpdate($txt)
  598. {
  599. $bodyNode = new ProtocolNode("body", NULL, NULL, $txt);
  600. $serverNode = new ProtocolNode("server", NULL, NULL, "");
  601. $xHash = array();
  602. $xHash["xmlns"] = "jabber:x:event";
  603. $xNode = new ProtocolNode("x", $xHash, array($serverNode), "");
  604. $messageHash = array();
  605. $messageHash["to"] = 's.us';
  606. $messageHash["type"] = "chat";
  607. $messageHash["id"] = $this->msgId();
  608. $messsageNode = new ProtocolNode("message", $messageHash, array($xNode, $bodyNode), "");
  609. $this->sendNode($messsageNode);
  610. $this->eventManager()->fire('onSendStatusUpdate', array($this->_phoneNumber, $txt));
  611. }
  612. /**
  613. * Send the composing message status. When typing a message.
  614. *
  615. * @param $to
  616. * The reciepient to send.
  617. */
  618. public function sendComposingMessage($to)
  619. {
  620. $comphash = array();
  621. $comphash['xmlns'] = 'http://jabber.org/protocol/chatstates';
  622. $compose = new ProtocolNode("composing", $comphash, NULL, "");
  623. $whatsAppServer = WhatsProt::_whatsAppServer;
  624. if (strpos($to, "-") !== FALSE) {
  625. $whatsAppServer = WhatsProt::_whatsAppGroupServer;
  626. }
  627. $messageHash = array();
  628. $messageHash["to"] = $to . "@" . $whatsAppServer;
  629. $messageHash["type"] = "chat";
  630. $messageHash["id"] = $this->msgId();
  631. $messageHash["t"] = time();
  632. $messageNode = new ProtocolNode("message", $messageHash, array($compose), "");
  633. $this->sendNode($messageNode);
  634. }
  635. /**
  636. * Send the composing message status. When make a pause typing a message.
  637. *
  638. * @param $to
  639. * The reciepient to send.
  640. */
  641. public function sendPausedMessage($to)
  642. {
  643. $comphash = array();
  644. $comphash['xmlns'] = 'http://jabber.org/protocol/chatstates';
  645. $compose = new ProtocolNode("paused", $comphash, NULL, "");
  646. $whatsAppServer = WhatsProt::_whatsAppServer;
  647. if (strpos($to, "-") !== FALSE) {
  648. $whatsAppServer = WhatsProt::_whatsAppGroupServer;
  649. }
  650. $messageHash = array();
  651. $messageHash["to"] = $to . "@" . $whatsAppServer;
  652. $messageHash["type"] = "chat";
  653. $messageHash["id"] = $this->msgId();
  654. $messageHash["t"] = time();
  655. $messageNode = new ProtocolNode("message", $messageHash, array($compose), "");
  656. $this->sendNode($messageNode);
  657. }
  658. /**
  659. * Create a group chat.
  660. *
  661. * @param string $subject
  662. * The reciepient to send.
  663. * @param array $participants
  664. * An array with the participans.
  665. *
  666. * @return string
  667. * The group ID.
  668. */
  669. public function createGroupChat($subject, $participants)
  670. {
  671. $groupHash = array();
  672. $groupHash["xmlns"] = "w:g";
  673. $groupHash["action"] = "create";
  674. $groupHash["subject"] = $subject;
  675. $group = new ProtocolNode("group", $groupHash, NULL, "");
  676. $setHash = array();
  677. $setHash["id"] = $this->msgId();
  678. $setHash["type"] = "set";
  679. $setHash["to"] = WhatsProt::_whatsAppGroupServer;
  680. $groupNode = new ProtocolNode("iq", $setHash, array($group), "");
  681. $this->sendNode($groupNode);
  682. $this->WaitforGroupId();
  683. $groupId = $this->_lastGroupId;
  684. $this->addGroupParticipants($groupId, $participants);
  685. return $groupId;
  686. }
  687. /**
  688. * Add participants to a group.
  689. *
  690. * @param string $groupId
  691. * The group ID.
  692. * @param array $participants
  693. * An array with the participans.
  694. */
  695. public function addGroupParticipants($groupId, $participants)
  696. {
  697. $this->SendActionGroupParticipants($groupId, $participants, 'add');
  698. }
  699. /**
  700. * Remove participants from a group.
  701. *
  702. * @param string $groupId
  703. * The group ID.
  704. * @param array $participants
  705. * An array with the participans.
  706. */
  707. public function removeGroupParticipants($groupId, $participants)
  708. {
  709. $this->SendActionGroupParticipants($groupId, $participants, 'remove');
  710. }
  711. /**
  712. * Sent anctio to participants of a group.
  713. *
  714. * @param string $groupId
  715. * The group ID.
  716. * @param array $participants
  717. * An array with the participans.
  718. * @param string $tag
  719. * The tag action.
  720. */
  721. protected function sendActionGroupParticipants($groupId, $participants, $tag)
  722. {
  723. $Participants = array();
  724. foreach($participants as $participant) {
  725. $Participants[] = new ProtocolNode("participant", array("jid" => $participant . '@' . WhatsProt::_whatsAppServer), NULL, "");
  726. }
  727. $childHash = array();
  728. $childHash["xmlns"] = "w:g";
  729. $child = new ProtocolNode($tag, $childHash, $Participants, "");
  730. $setHash = array();
  731. $setHash["id"] = $this->msgId();
  732. $setHash["type"] = "set";
  733. $setHash["to"] = $groupId . '@' . WhatsProt::_whatsAppGroupServer;
  734. $node = new ProtocolNode("iq", $setHash, array($child), "");
  735. $this->sendNode($node);
  736. }
  737. /**
  738. * Send a text message to the user/group.
  739. *
  740. * @param $to
  741. * The reciepient to send.
  742. * @param $txt
  743. * The text message.
  744. */
  745. public function Message($to, $txt)
  746. {
  747. $bodyNode = new ProtocolNode("body", NULL, NULL, $txt);
  748. $this->SendMessageNode($to, $bodyNode);
  749. }
  750. /**
  751. * Send a image to the user/group.
  752. *
  753. * @param $to
  754. * The reciepient to send.
  755. * @param $file
  756. * The url/uri to the image.
  757. */
  758. public function MessageImage($to, $file)
  759. {
  760. if ($image = file_get_contents($file)) {
  761. $fileName = basename($file);
  762. if (!preg_match("/https:\/\/[a-z0-9]+\.whatsapp.net\//i", $file)) {
  763. $uri = "/tmp/" . md5(time()) . $fileName;
  764. $tmpFile = file_put_contents($uri, $image);
  765. $url = $this->uploadFile($uri);
  766. unlink($uri);
  767. } else {
  768. $url = $file;
  769. }
  770. $mediaAttribs = array();
  771. $mediaAttribs["xmlns"] = "urn:xmpp:whatsapp:mms";
  772. $mediaAttribs["type"] = "image";
  773. $mediaAttribs["url"] = $url;
  774. $mediaAttribs["file"] = $fileName;
  775. $mediaAttribs["size"] = strlen($image);
  776. $icon = createIcon($image);
  777. $mediaNode = new ProtocolNode("media", $mediaAttribs, NULL, $icon);
  778. $this->SendMessageNode($to, $mediaNode);
  779. } else {
  780. throw new Exception('A problem has occurred trying to get the image.');
  781. }
  782. }
  783. /**
  784. * Send a video to the user/group.
  785. *
  786. * @param $to
  787. * The reciepient to send.
  788. * @param $file
  789. * The url/uri to the MP4/MOV video.
  790. */
  791. public function MessageVideo($to, $file)
  792. {
  793. $extension = strtolower(pathinfo($url, PATHINFO_EXTENSION));
  794. $allowedExtensions = array('mp4', 'mov');
  795. if (!in_array($extension, $allowedExtensions)) {
  796. throw new Exception('Unsupported video format.');
  797. } elseif ($image = file_get_contents($file)) {
  798. $fileName = basename($file);
  799. if (!preg_match("/https:\/\/[a-z0-9]+\.whatsapp.net\//i", $file)) {
  800. $uri = "/tmp/" . md5(time()) . $fileName;
  801. $tmpFile = file_put_contents($uri, $image);
  802. $url = $this->uploadFile($uri);
  803. unlink($uri);
  804. } else {
  805. $url = $file;
  806. }
  807. $mediaAttribs = array();
  808. $mediaAttribs["xmlns"] = "urn:xmpp:whatsapp:mms";
  809. $mediaAttribs["type"] = "video";
  810. $mediaAttribs["url"] = $url;
  811. $mediaAttribs["file"] = $fileName;
  812. $mediaAttribs["size"] = strlen($image);
  813. $icon = createVideoIcon($image);
  814. $mediaNode = new ProtocolNode("media", $mediaAttribs, NULL, $icon);
  815. $this->SendMessageNode($to, $mediaNode);
  816. } else {
  817. throw new Exception('A problem has occurred trying to get the video.');
  818. }
  819. }
  820. /**
  821. * Send a audio to the user/group.
  822. *
  823. * @param $to
  824. * The reciepient to send.
  825. * @param $file
  826. * The url/uri to the 3GP/CAF audio.
  827. */
  828. public function MessageAudio($to, $file)
  829. {
  830. $extension = strtolower(pathinfo($url, PATHINFO_EXTENSION));
  831. $allowedExtensions = array('3gp', 'caf');
  832. if (!in_array($extension, $allowedExtensions)) {
  833. throw new Exception('Unsupported audio format.');
  834. } elseif ($image = file_get_contents($file)) {
  835. $fileName = basename($file);
  836. if (!preg_match("/https:\/\/[a-z0-9]+\.whatsapp.net\//i", $file)) {
  837. $uri = "/tmp/" . md5(time()) . $fileName;
  838. $tmpFile = file_put_contents($uri, $image);
  839. $url = $this->uploadFile($uri);
  840. unlink($uri);
  841. } else {
  842. $url = $file;
  843. }
  844. $mediaAttribs = array();
  845. $mediaAttribs["xmlns"] = "urn:xmpp:whatsapp:mms";
  846. $mediaAttribs["type"] = "audio";
  847. $mediaAttribs["url"] = $url;
  848. $mediaAttribs["file"] = $fileName;
  849. $mediaAttribs["size"] = strlen($image);
  850. $mediaNode = new ProtocolNode("media", $mediaAttribs, NULL, "");
  851. $this->SendMessageNode($to, $mediaNode);
  852. } else {
  853. throw new Exception('A problem has occurred trying to get the audio.');
  854. }
  855. }
  856. /**
  857. * Send a vCard to the user/group.
  858. *
  859. * @param $to
  860. * The reciepient to send.
  861. * @param $name
  862. * The contact name.
  863. * @param $vCard
  864. * The contact vCard to send.
  865. */
  866. public function vCard($to, $name, $vCard)
  867. {
  868. $vCardAttribs = array();
  869. $vCardAttribs['name'] = $name;
  870. $vCardNode = new ProtocolNode("vcard", $vCardAttribs, NULL, $vCard);
  871. $mediaAttribs = array();
  872. $mediaAttribs["xmlns"] = "urn:xmpp:whatsapp:mms";
  873. $mediaAttribs["type"] = "vcard";
  874. $mediaAttribs["encoding"] = "text";
  875. $mediaNode = new ProtocolNode("media", $mediaAttribs, array($vCardNode), "");
  876. $this->SendMessageNode($to, $mediaNode);
  877. }
  878. /**
  879. * Send a location to the user/group.
  880. *
  881. * @param $to
  882. * The reciepient to send.
  883. * @param $long
  884. * The logitude to send.
  885. * @param $lat
  886. * The latitude to send.
  887. */
  888. public function Location($to, $long, $lat)
  889. {
  890. $mediaHash = array();
  891. $mediaHash['xmlns'] = "urn:xmpp:whatsapp:mms";
  892. $mediaHash['type'] = "location";
  893. $mediaHash['latitude'] = $lat;
  894. $mediaHash['longitude'] = $long;
  895. $mediaNode = new ProtocolNode("media", $mediaHash, NULL, NULL);
  896. $this->SendMessageNode($to, $mediaNode);
  897. }
  898. /**
  899. * Send a location to the user/group.
  900. *
  901. * @param $to
  902. * The reciepient to send.
  903. * @param $url
  904. * The google maps place url.
  905. * @param $long
  906. * The logitude to send.
  907. * @param $lat
  908. * The latitude to send.
  909. * @param $name
  910. * The google maps place name.
  911. * @param $image
  912. * The google maps place image.
  913. *
  914. * @see: https://maps.google.com/maps/place?cid=1421139585205719654
  915. * @todo: Add support for only pass as argument the place id.
  916. */
  917. public function Place($to, $url, $long, $lat, $name, $image)
  918. {
  919. $mediaHash = array();
  920. $mediaHash['xmlns'] = "urn:xmpp:whatsapp:mms";
  921. $mediaHash['type'] = "location";
  922. $mediaHash['url'] = $url;
  923. $mediaHash['latitude'] = $lat;
  924. $mediaHash['longitude'] = $long;
  925. if ($image = file_get_contents($file))
  926. {
  927. $icon = createVideoIcon($image);
  928. } else {
  929. $icon = giftThumbnail();
  930. }
  931. $mediaNode = new ProtocolNode("media", $mediaHash, NULL, $icon);
  932. $this->SendMessageNode($to, $mediaNode);
  933. }
  934. /**
  935. * Upload file to WhatsApp servers.
  936. *
  937. * @param $file
  938. * The uri of the file.
  939. *
  940. * @return string
  941. * Return the remote url.
  942. */
  943. public function uploadFile($file)
  944. {
  945. $data['file'] = "@" . $file;
  946. $ch = curl_init();
  947. curl_setopt($ch, CURLOPT_HEADER, 0);
  948. curl_setopt($ch, CURLOPT_HTTPHEADER, array( 'Expect:' ) );
  949. curl_setopt($ch, CURLOPT_URL, WhatsProt::_whatsAppUploadHost);
  950. curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  951. curl_setopt($ch, CURLOPT_POST, true);
  952. curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
  953. $response = curl_exec($ch);
  954. curl_close($ch);
  955. $xml = simplexml_load_string($response);
  956. $url = strip_tags($xml->dict->string[3]->asXML());
  957. if (!empty($url)) {
  958. $this->eventManager()->fire('onUploadFile', array($this->_phoneNumber, basename($file), $url));
  959. return $url;
  960. } else {
  961. $this->eventManager()->fire('onFailedUploadFile', array($this->_phoneNumber, basename($file)));
  962. return FALSE;
  963. }
  964. }
  965. /**
  966. * Request to retrieve the last online string.
  967. *
  968. * @param $to
  969. * The reciepient to get the last seen.
  970. */
  971. public function RequestLastSeen($to)
  972. {
  973. $whatsAppServer = WhatsProt::_whatsAppServer;
  974. $queryHash = array();
  975. $queryHash['xmlns'] = "jabber:iq:last";
  976. $queryNode = new ProtocolNode("query", $queryHash, NULL, NULL);
  977. $messageHash = array();
  978. $messageHash["to"] = $to . "@" . $whatsAppServer;
  979. $messageHash["type"] = "get";
  980. $messageHash["id"] = $this->msgId();
  981. $messageHash["from"] = $this->_phoneNumber . "@" . WhatsProt::_whatsAppServer;
  982. $messsageNode = new ProtocolNode("iq", $messageHash, array($queryNode), "");
  983. $this->sendNode($messsageNode);
  984. $this->eventManager()->fire('onRequestLastSeen', array($this->_phoneNumber, $messageHash["id"], $to));
  985. }
  986. /**
  987. * Send a pong to the whatsapp server.
  988. *
  989. * @param $msgid
  990. * The id of the message.
  991. */
  992. public function Pong($msgid)
  993. {
  994. $whatsAppServer = WhatsProt::_whatsAppServer;
  995. $messageHash = array();
  996. $messageHash["to"] = $whatsAppServer;
  997. $messageHash["id"] = $msgid;
  998. $messageHash["type"] = "result";
  999. $messsageNode = new ProtocolNode("iq", $messageHash, NULL, "");
  1000. $this->sendNode($messsageNode);
  1001. $this->eventManager()->fire('onPong', array($this->_phoneNumber, $msgid));
  1002. }
  1003. /**
  1004. * Control msg id.
  1005. *
  1006. * @return string
  1007. * A message id string.
  1008. */
  1009. protected function msgId()
  1010. {
  1011. $msgid = time() . '-' . $this->_msgCounter;
  1012. $this->_msgCounter++;
  1013. return $msgid;
  1014. }
  1015. /**
  1016. * Request a registration code from WhatsApp.
  1017. *
  1018. * @param $method
  1019. * Accepts only 'sms' or 'voice' as a value.
  1020. * @param $countryCody
  1021. * ISO Country Code, 2 Digit.
  1022. * @param $langCode
  1023. * ISO 639-1 Language Code: two-letter codes.
  1024. *
  1025. * @return object
  1026. * An object with server response.
  1027. * - status: Status of the request (sent/fail).
  1028. * - length: Registration code lenght.
  1029. * - method: Used method.
  1030. * - reason: Reason of the status (e.g. too_recent/missing_param/bad_param).
  1031. * - param: The missing_param/bad_param.
  1032. * - retry_after: Waiting time before requesting a new code.
  1033. */
  1034. public function requestCode($method = 'sms', $countryCode = 'US', $langCode = 'en')
  1035. {
  1036. if (!$phone = $this->dissectPhone()) {
  1037. throw new Exception('The prived phone number is not valid.');
  1038. return FALSE;
  1039. }
  1040. // Build the token.
  1041. $token = md5(WhatsProt::_whatsAppToken . $phone['phone']);
  1042. // Build the url.
  1043. $host = 'https://' . WhatsProt::_whatsAppReqHost;
  1044. $query = array(
  1045. 'cc' => $phone['cc'],
  1046. 'in' => $phone['phone'],
  1047. 'lc' => $countryCode,
  1048. 'lg' => $langCode,
  1049. 'mcc' => '000',
  1050. 'mnc' => '000',
  1051. 'method' => $method,
  1052. 'id' => $this->_identity,
  1053. 'token' => $token,
  1054. 'c' => 'cookie',
  1055. );
  1056. $response = $this->getResponse($host, $query);
  1057. if ($response->status != 'sent') {
  1058. $this->eventManager()->fire('onFailedRequestCode', array($this->_phoneNumber, $method, $response->reason, $response->reason == 'too_recent' ? $response->reason : $response->param));
  1059. throw new Exception('There was a problem trying to request the code.');
  1060. } else {
  1061. $this->eventManager()->fire('onRequestCode', array($this->_phoneNumber, $method, $response->length));
  1062. }
  1063. return $response;
  1064. }
  1065. /*
  1066. * Register account on WhatsApp using the provided code.
  1067. *
  1068. * @param integer $code
  1069. * Numeric code value provided on requestCode().
  1070. *
  1071. * @return object
  1072. * An object with server response.
  1073. * - status: Account status.
  1074. * - login: Phone number with country code.
  1075. * - pw: Account password.
  1076. * - type: Type of account.
  1077. * - expiration: Expiration date in UNIX TimeStamp.
  1078. * - kind: Kind of account.
  1079. * - price: Formated price of account.
  1080. * - cost: Decimal amount of account.
  1081. * - currency: Currency price of account.
  1082. * - price_expiration: Price expiration in UNIX TimeStamp.
  1083. */
  1084. public function registerCode($code)
  1085. {
  1086. if (!$phone = $this->dissectPhone()) {
  1087. throw new Exception('The prived phone number is not valid.');
  1088. return FALSE;
  1089. }
  1090. // Build the url.
  1091. $host = 'https://' . WhatsProt::_whatsAppRegHost;
  1092. $query = array(
  1093. 'cc' => $phone['cc'],
  1094. 'in' => $phone['phone'],
  1095. 'id' => $this->_identity,
  1096. 'code' => $code,
  1097. 'c' => 'cookie',
  1098. );
  1099. $response = $this->getResponse($host, $query);
  1100. if ($response->status != 'ok') {
  1101. $this->eventManager()->fire('onFailedRegisterCode', array($this->_phoneNumber, $response->status, $response->reason, $response->retry_after));
  1102. throw new Exception('An error occurred registering the registration code from WhatsApp.');
  1103. } else {
  1104. $this->eventManager()->fire('onRegisterCode', array(
  1105. $this->_phoneNumber,
  1106. $response->login,
  1107. $response->pw,
  1108. $response->type,
  1109. $response->expiration,
  1110. $response->kind,
  1111. $response->price,
  1112. $response->cost,
  1113. $response->currency,
  1114. $response->price_expiration
  1115. ));
  1116. }
  1117. return $response;
  1118. }
  1119. /*
  1120. * Check if account credentials are valid.
  1121. *
  1122. * WARNING: WhatsApp now changes your password everytime you use this.
  1123. * Make sure you update your config file if the output informs about
  1124. * a password change.
  1125. *
  1126. * @return object
  1127. * An object with server response.
  1128. * - status: Account status.
  1129. * - login: Phone number with country code.
  1130. * - pw: Account password.
  1131. * - type: Type of account.
  1132. * - expiration: Expiration date in UNIX TimeStamp.
  1133. * - kind: Kind of account.
  1134. * - price: Formated price of account.
  1135. * - cost: Decimal amount of account.
  1136. * - currency: Currency price of account.
  1137. * - price_expiration: Price expiration in UNIX TimeStamp.
  1138. */
  1139. public function checkCredentials()
  1140. {
  1141. if (!$phone = $this->dissectPhone()) {
  1142. throw new Exception('The prived phone number is not valid.');
  1143. return FALSE;
  1144. }
  1145. // Build the url.
  1146. $host = 'https://' . WhatsProt::_whatsAppCheHost;
  1147. $query = array(
  1148. 'cc' => $phone['cc'],
  1149. 'in' => $phone['phone'],
  1150. 'udid' => $this->_identity,
  1151. 'c' => 'cookie',
  1152. );
  1153. $response = $this->getResponse($host, $query);
  1154. if ($response->status != 'ok') {
  1155. $this->eventManager()->fire('onBadCredentials', array($this->_phoneNumber, $response->status, $response->reason));
  1156. throw new Exception('There was a problem trying to request the code.');
  1157. } else {
  1158. $this->eventManager()->fire('onGoodCredentials', array(
  1159. $this->_phoneNumber,
  1160. $response->login,
  1161. $response->pw,
  1162. $response->type,
  1163. $response->expiration,
  1164. $response->kind,
  1165. $response->price,
  1166. $response->cost,
  1167. $response->currency,
  1168. $response->price_expiration
  1169. ));
  1170. }
  1171. return $response;
  1172. }
  1173. protected function getResponse($host, $query)
  1174. {
  1175. // Build the url.
  1176. $url = $host . '?';
  1177. foreach ($query as $key => $value) {
  1178. $url .= $key . '=' . $value . '&';
  1179. }
  1180. rtrim($url, '&');
  1181. // Open connection.
  1182. $ch = curl_init();
  1183. // Configure the connection.
  1184. curl_setopt($ch, CURLOPT_URL, $url);
  1185. curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
  1186. curl_setopt($ch, CURLOPT_HEADER, 0);
  1187. curl_setopt($ch, CURLOPT_USERAGENT, WhatsProt::_whatsAppUserAgent);
  1188. curl_setopt($ch, CURLOPT_HTTPHEADER, array('Accept: text/json'));
  1189. // This makes CURL accept any peer!
  1190. curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
  1191. // Get the response.
  1192. $response = curl_exec($ch);
  1193. // Close the connection.
  1194. curl_close($ch);
  1195. return json_decode($response);
  1196. }
  1197. /**
  1198. * Dissect country code from phone number.
  1199. *
  1200. * @return array
  1201. * An associative array with country code and phone number.
  1202. * - country: The detected country name.
  1203. * - cc: The detected country code.
  1204. * - phone: The phone number.
  1205. * Return FALSE if country code is not found.
  1206. */
  1207. protected function dissectPhone()
  1208. {
  1209. if (($handle = fopen('countries.csv', 'rb')) !== FALSE) {
  1210. while (($data = fgetcsv($handle, 1000)) !== FALSE) {
  1211. if (strpos($this->_phoneNumber, $data[1]) === 0) {
  1212. // Return the first appearance.
  1213. fclose($handle);
  1214. $phone = array(
  1215. 'country' => $data[0],
  1216. 'cc' => $data[1],
  1217. 'phone' => substr($this->_phoneNumber, strlen($data[1]), strlen($this->_phoneNumber)),
  1218. );
  1219. $this->eventManager()->fire('onDissectPhone', array_merge(array($this->_phoneNumber), $phone));
  1220. return $phone;
  1221. }
  1222. }
  1223. fclose($handle);
  1224. }
  1225. $this->eventManager()->fire('onFailedDissectPhone', array($this->_phoneNumber));
  1226. return FALSE;
  1227. }
  1228. /**
  1229. * Print a message to the debug console.
  1230. *
  1231. * @param $debugMsg
  1232. * The debug message.
  1233. */
  1234. protected function DebugPrint($debugMsg)
  1235. {
  1236. if ($this->_debug) {
  1237. print($debugMsg);
  1238. }
  1239. }
  1240. /**
  1241. * Gets a new micro event dispatcher.
  1242. */
  1243. public function eventManager()
  1244. {
  1245. if (!is_object($this->event)) {
  1246. $this->event = new WhatsAppEvent();
  1247. }
  1248. return $this->event;
  1249. }
  1250. }