Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Lots of changes, new outqueue system, auto msgid, auto send nickname …

…and presence, node processing, and more see testprotocol.php for more details
  • Loading branch information...
commit ca4be92b37044e028bb01e349734d5fc33233fc3 1 parent 952e377
Martí Planellas beldar authored
20 src/php/protocol.class.php
View
@@ -21,7 +21,7 @@ public function getInput()
class ProtocolNode
{
- public $_tag;
+ public $_tag;
public $_attributeHash;
public $_children;
public $_data;
@@ -91,6 +91,24 @@ public function getChild($tag)
}
return NULL;
}
+
+ public function hasChild($tag)
+ {
+ return $this->getChild($tag)==null ? false : true;
+ }
+
+ public function refreshTimes($offset=0)
+ {
+ if(isset($this->_attributeHash['id']))
+ {
+ $id = $this->_attributeHash['id'];
+ $parts = explode('-',$id);
+ $parts[0] = time()+$offset;
+ $this->_attributeHash['id'] = implode('-',$parts);
+ }
+ if(isset($this->_attributeHash['t']))
+ $this->_attributeHash['t'] = time();
+ }
}
class BinTreeNodeReader
64 src/php/testprotocol.php
View
@@ -5,27 +5,25 @@
# IMEI here as it is!
$options = getopt("d::", array("debug::"));
$debug = (array_key_exists("debug", $options) || array_key_exists("d", $options)) ? true : false;
-$w = new WhatsProt("*****", "****", "John Doe", $debug);
+# Target phone number
+$target = "**********";
+
+$w = new WhatsProt("************", "********", "John Doe", true);
$w->Connect();
+# Now Login function sends Nickname and (Available) Presence
$w->Login();
-$w->SendPrecense('available');
-$w->Message(time() . "-1", "****", "Hola");
-$w->WaitforReceipt();
-# To send an image, put the image on the internet somewhere and send it with the MessageImage function.
-# if you want to be real fancy then take a scaled down version (100pixel) and base64_encode it,
-# and send that as the last parameter to the MessageImage function.
-# I wrote a quick function (funcs.php->createIcon) to do all this for you.
-# You can also just leave the last param empty to send no icon
-# Obviously this needs better integration... but this is a start...
-# Also thumb.jpgb64 is just for an example!
-$iconfile = "../../tests/thumb.jpgb64";
-$fp = fopen($iconfile, "r");
-$icon = fread($fp, filesize($iconfile));
-fclose($fp);
+# Implemented out queue messages and auto msgid
+$w->Message($target, "1");
+$w->Message($target, "2");
+$w->Message($target, "3");
+$w->Message($target, "4");
+$w->Message($target, "5");
+# You can create a ProcessNode class (or whatever name you want) that has a process($node) function
+# and pass it through setNewMessageBind, that way everytime the class receives a text message it will run
+# the process function to it.
+$pn = new ProcessNode($w,$target);
+$w->setNewMessageBind($pn);
-$w->MessageImage(time() . "-1", "*****", "https://lh3.googleusercontent.com/-vT0wjhrlTaQ/T_bwd4_PUYI/AAAAAAAABog/oKPZ6ssJqC0/s673/DSC02471.JPG", "DSC02471.jpg", 55508, $icon);
-$w->WaitforReceipt();
-$w->MessageImage(time() . "-1", "******", "https://lh3.googleusercontent.com/-vT0wjhrlTaQ/T_bwd4_PUYI/AAAAAAAABog/oKPZ6ssJqC0/s673/DSC02471.JPG", "DSC02471.jpg", 55508, $icon);
while(1)
{
$w->PollMessages();
@@ -33,8 +31,36 @@
foreach ($msgs as $m)
{
# process inbound messages
- print($m->NodeString("") . "\n");
+ //print($m->NodeString("") . "\n");
+ }
+}
+
+class ProcessNode{
+ protected $_wp = false;
+ protected $_target = false;
+ function __construct($wp,$target)
+ {
+ $this->_wp = $wp;
+ $this->_target = $target;
+ }
+ public function process($node)
+ {
+ # Example of process function, you have to guess a number (psss it's 5)
+ # If you guess it right you get a gift
+ $text = $node->getChild('body');
+ $text = $text->_data;
+ if($text && ($text == "5" || trim($text)=="5")){
+ $iconfile = "../../tests/Gift.jpgb64";
+ $fp = fopen($iconfile, "r");
+ $icon = fread($fp, filesize($iconfile));
+ fclose($fp);
+ $this->_wp->MessageImage($this->_target, "https://mms604.whatsapp.net/d11/26/09/8/5/85a13e7812a5e7ad1f8071319d9d1b43.jpg", "hero.jpg", 84712, $icon);
+ $this->_wp->Message($this->_target, "¡Congratulations you guessed the right number!");
+ }else{
+ $this->_wp->Message($this->_target, "¡I'm sorry, try again!");
+ }
}
+
}
?>
142 src/php/whatsprot.class.php
View
@@ -24,7 +24,9 @@ class WhatsProt
protected $_accountinfo;
protected $_messageQueue = array();
-
+ protected $_outQueue = array();
+ protected $_lastId = false;
+ protected $_msgCounter = 1;
protected $_socket;
protected $_writer;
protected $_reader;
@@ -68,11 +70,12 @@ protected function addAuth()
public function encryptPassword()
{
if(stripos($this->_imei, ":") !== false){
- $this->_imei = strtoupper($this->_imei);
- return md5($this->_imei.$this->_imei);
+ $this->_imei = strtoupper($this->_imei);
+ return md5($this->_imei.$this->_imei);
}
- else {
- return md5(strrev($this->_imei));
+ else
+ {
+ return md5(strrev($this->_imei));
}
}
@@ -85,6 +88,16 @@ protected function authenticate()
$response = $this->_outputKey->encode($array, 0, strlen($array), false);
return $response;
}
+
+ public function setNewMessageBind($bind)
+ {
+ $this->_newmsgBind = $bind;
+ }
+
+ public function addOutQueue($node)
+ {
+ $this->_outQueue[] = $node;
+ }
protected function addAuthResponse()
{
@@ -126,25 +139,23 @@ protected function processChallenge($node)
protected function sendMessageReceived($msg)
{
$requestNode = $msg->getChild("request");
- if ($requestNode != null)
+ $receivedNode = $msg->getChild("received");
+ if ($requestNode != null || $receivedNode != null)
{
- $xmlnsAttrib = $requestNode->getAttribute("xmlns");
- if (strcmp($xmlnsAttrib, "urn:xmpp:receipts") == 0)
- {
- $recievedHash = array();
- $recievedHash["xmlns"] = "urn:xmpp:receipts";
- $receivedNode = new ProtocolNode("received", $recievedHash, null, "");
-
- $messageHash = array();
- $messageHash["to"] = $msg->getAttribute("from");
- $messageHash["type"] = "chat";
- $messageHash["id"] = $msg->getAttribute("id");
- $messageNode = new ProtocolNode("message", $messageHash, array($receivedNode), "");
- $this->sendNode($messageNode);
- }
+ $recievedHash = array();
+ $recievedHash["xmlns"] = "urn:xmpp:receipts";
+ $receivedNode = new ProtocolNode("received", $recievedHash, null, "");
+
+ $messageHash = array();
+ $messageHash["to"] = $msg->getAttribute("from");
+ $messageHash["type"] = "chat";
+ $messageHash["id"] = $msg->getAttribute("id");
+ $messageHash["t"] = time();
+ $messageNode = new ProtocolNode("message", $messageHash, array($receivedNode), "");
+ $this->sendNode($messageNode);
}
}
-
+
protected function processInboundData($data)
{
try
@@ -166,6 +177,10 @@ protected function processInboundData($data)
{
array_push($this->_messageQueue, $node);
$this->sendMessageReceived($node);
+ if($node->hasChild('x') && $this->_lastId==$node->getAttribute('id'))
+ $this->sendNext();
+ if($this->_newmsgBind && $node->getChild('body'))
+ $this->_newmsgBind->process($node);
}
if (strcmp($node->_tag, "iq") == 0 AND strcmp($node->_attributeHash['type'], "get") == 0 AND strcmp($node->_children[0]->_tag, "ping") == 0)
{
@@ -184,12 +199,39 @@ protected function processInboundData($data)
}
}
+ public function sendNext()
+ {
+ if(count($this->_outQueue)>0)
+ {
+ $msgnode = array_shift($this->_outQueue);
+ $msgnode->refreshTimes();
+ $this->_lastId = $msgnode->getAttribute('id');
+ $this->sendNode($msgnode);
+ }else
+ $this->_lastId = false;
+ }
+
+ public function sendComposing($msg)
+ {
+ $comphash = array();
+ $comphash['xmlns'] = "http://jabber.org/protocol/chatstates";
+ $compose = new ProtocolNode("composing", $comphash, null, "");
+ $messageHash = array();
+ $messageHash["to"] = $msg->getAttribute("from");
+ $messageHash["type"] = "chat";
+ $messageHash["id"] = time().'-'.$this->_msgCounter;
+ $messageHash["t"] = time();
+ $this->_msgCounter++;
+ $messageNode = new ProtocolNode("message", $messageHash, array($compose), "");
+ $this->sendNode($messageNode);
+ }
+
public function accountInfo(){
if(is_array($this->_accountinfo)){
- print_r($this->_accountinfo);
+ print_r($this->_accountinfo);
}
else{
- echo "No information available";
+ echo "No information available";
}
}
@@ -220,6 +262,8 @@ public function Login()
{
$this->processInboundData($this->readData());
} while (($cnt++ < 100) && (strcmp($this->_loginStatus, $this->_disconnectedStatus) == 0));
+ $this->sendNickname();
+ $this->SendPresence();
}
# Pull from the socket, and place incoming messages in the message queue
@@ -250,13 +294,13 @@ public function WaitforReceipt()
$received = true;
}
}
- print($m->NodeString("") . "\n");
+ //print($m->NodeString("") . "\n");
}
}while(!$received);
- echo "Received node!!\n";
+ //echo "Received node!!\n";
}
- public function SendPrecense($type="available")
+ public function SendPresence($type="available")
{
$presence = array();
$presence['type'] = $type;
@@ -265,34 +309,41 @@ public function SendPrecense($type="available")
$this->sendNode($node);
}
- protected function SendMessageNode($msgid, $to, $node)
+ protected function SendMessageNode($to, $node)
{
$serverNode = new ProtocolNode("server", null, null, "");
$xHash = array();
$xHash["xmlns"] = "jabber:x:event";
$xNode = new ProtocolNode("x", $xHash, array($serverNode), "");
$notify = array();
- $notify['urn:xmpp:whatsapp'];
+ $notify['xmlns'] = 'urn:xmpp:whatsapp';
$notify['name'] = $this->_name;
$notnode = new ProtocolNode("notify", $notify, null, "");
$request = array();
$request['xmlns'] = "urn:xmpp:receipts";
$reqnode = new ProtocolNode("request", $request, null, "");
+ $msgid = time().'-'.$this->_msgCounter;
$messageHash = array();
$messageHash["to"] = $to . "@" . $this->_whatsAppServer;
$messageHash["type"] = "chat";
$messageHash["id"] = $msgid;
+ $messageHash["t"] = time();
+ $this->_msgCounter++;
$messsageNode = new ProtocolNode("message", $messageHash, array($xNode, $notnode,$reqnode,$node), "");
- $this->sendNode($messsageNode);
+ if(!$this->_lastId){
+ $this->_lastId = $msgid;
+ $this->sendNode($messsageNode);
+ }else
+ $this->_outQueue[] = $messsageNode;
}
- public function Message($msgid, $to, $txt)
+ public function Message($to, $txt)
{
$bodyNode = new ProtocolNode("body", null, null, $txt);
- $this->SendMessageNode($msgid, $to, $bodyNode);
+ $this->SendMessageNode($to, $bodyNode);
}
- public function MessageImage($msgid, $to, $url, $file, $size, $icon)
+ public function MessageImage($to, $url, $file, $size, $icon)
{
$mediaAttribs = array();
$mediaAttribs["xmlns"] = "urn:xmpp:whatsapp:mms";
@@ -302,7 +353,7 @@ public function MessageImage($msgid, $to, $url, $file, $size, $icon)
$mediaAttribs["size"] = $size;
$mediaNode = new ProtocolNode("media", $mediaAttribs, null, $icon);
- $this->SendMessageNode($msgid, $to, $mediaNode);
+ $this->SendMessageNode($to, $mediaNode);
}
public function Location($msgid, $to, $long, $lat)
@@ -325,7 +376,22 @@ public function Location($msgid, $to, $long, $lat)
$messsageNode = new ProtocolNode("message", $messageHash, array($mediaNode), "");
$this->sendNode($messsageNode);
}
-
+
+ public function sendStatusUpdate($msgid, $txt)
+ {
+ $bodyNode = new ProtocolNode("body", null, null, $txt);
+ $serverNode = new ProtocolNode("server", null, null, "");
+ $xHash = array();
+ $xHash["xmlns"] = "jabber:x:event";
+ $xNode = new ProtocolNode("x", $xHash, array($serverNode), "");
+ $messageHash = array();
+ $messageHash["to"] = 's.us';
+ $messageHash["type"] = "chat";
+ $messageHash["id"] = $msgid;
+ $messsageNode = new ProtocolNode("message", $messageHash, array($xNode, $bodyNode), "");
+ $this->sendNode($messsageNode);
+ }
+
public function Pong($msgid)
{
$whatsAppServer = $this->_whatsAppServer;
@@ -338,6 +404,14 @@ public function Pong($msgid)
$messsageNode = new ProtocolNode("iq", $messageHash, null, "");
$this->sendNode($messsageNode);
}
+
+ public function sendNickname()
+ {
+ $messageHash = array();
+ $messageHash["name"] = $this->_name;
+ $messsageNode = new ProtocolNode("presence", $messageHash, null, "");
+ $this->sendNode($messsageNode);
+ }
protected function DebugPrint($debugMsg)
{
1  tests/Gift.jpgb64
View
@@ -0,0 +1 @@
+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
Please sign in to comment.
Something went wrong with that request. Please try again.