diff --git a/README.md b/README.md
index 3a8b0916..94e689e4 100644
--- a/README.md
+++ b/README.md
@@ -4,9 +4,9 @@
Created by [Daniil Gentili](https://daniil.it), licensed under AGPLv3.
-![MadelineProto logo](https://daniil.it/MadelineProto/logo.png)
+
-Logo created by [Matthew Hesketh](https://telegram.me/wrxck) (thanks again!).
+Logo created by [Matthew Hesketh](https://matthewhesketh.com) (thanks again!).
PHP implementation of MTProto, based on [telepy](https://github.com/griganton/telepy_old).
diff --git a/docs/index.md b/docs/index.md
index b66a0ea9..e7f59d70 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -8,9 +8,9 @@ description: PHP implementation of telegram's MTProto protocol
Created by [Daniil Gentili](https://daniil.it), licensed under AGPLv3.
-![MadelineProto logo](https://daniil.it/MadelineProto/logo.png)
+
-Logo created by [Matthew Hesketh](https://telegram.me/wrxck) (thanks again!).
+Logo created by [Matthew Hesketh](https://matthewhesketh.com) (thanks again!).
PHP implementation of MTProto, based on [telepy](https://github.com/griganton/telepy_old).
diff --git a/src/danog/MadelineProto/MTProto.php b/src/danog/MadelineProto/MTProto.php
index fa1b6104..70856d1d 100644
--- a/src/danog/MadelineProto/MTProto.php
+++ b/src/danog/MadelineProto/MTProto.php
@@ -28,23 +28,8 @@ class MTProto extends PrimeModule
use \danog\MadelineProto\MTProtoTools\SeqNoHandler;
public $settings = [];
- public $authorized = false;
- public $waiting_code = false;
public $config = ['expires' => -1];
public $ipv6 = false;
- public $bad_msg_error_codes = [
- 16 => 'msg_id too low (most likely, client time is wrong; it would be worthwhile to synchronize it using msg_id notifications and re-send the original message with the “correct” msg_id or wrap it in a container with a new msg_id if the original message had waited too long on the client to be transmitted)',
- 17 => 'msg_id too high (similar to the previous case, the client time has to be synchronized, and the message re-sent with the correct msg_id)',
- 18 => 'incorrect two lower order msg_id bits (the server expects client message msg_id to be divisible by 4)',
- 19 => 'container msg_id is the same as msg_id of a previously received message (this must never happen)',
- 20 => 'message too old, and it cannot be verified whether the server has received a message with this msg_id or not',
- 32 => 'msg_seqno too low (the server has already received a message with a lower msg_id but with either a higher or an equal and odd seqno)',
- 33 => 'msg_seqno too high (similarly, there is a message with a higher msg_id but with either a lower or an equal and odd seqno)',
- 34 => 'an even msg_seqno expected (irrelevant message), but odd received',
- 35 => 'odd msg_seqno expected (relevant message), but even received',
- 48 => 'incorrect server salt (in this case, the bad_server_salt response is received with the correct salt, and the message is to be re-sent with it)',
- 64 => 'invalid container.',
- ];
public function __construct($settings = [])
{
@@ -238,6 +223,10 @@ Slv8kg9qv1m6XHVQY3PnEw+QQtqSIXklHwIDAQAB
\danog\MadelineProto\Logger::log('Resetting session id and seq_no in DC '.$id.'...');
$socket->session_id = \phpseclib\Crypt\Random::string(8);
$socket->seq_no = 0;
+ $socket->incoming_messages = [];
+ $socket->outgoing_messages = [];
+ $socket->new_outgoing = [];
+ $socket->new_incoming = [];
}
}
diff --git a/src/danog/MadelineProto/MTProtoTools/AuthKeyHandler.php b/src/danog/MadelineProto/MTProtoTools/AuthKeyHandler.php
index 097afe3f..30ace3d4 100644
--- a/src/danog/MadelineProto/MTProtoTools/AuthKeyHandler.php
+++ b/src/danog/MadelineProto/MTProtoTools/AuthKeyHandler.php
@@ -469,6 +469,9 @@ trait AuthKeyHandler
\danog\MadelineProto\Logger::log('An exception occurred while generating the authorization key: '.$e->getMessage().' Retrying (try number '.$retry_id_total.')...');
} catch (\danog\MadelineProto\RPCErrorException $e) {
\danog\MadelineProto\Logger::log('An RPCErrorException occurred while generating the authorization key: '.$e->getMessage().' Retrying (try number '.$retry_id_total.')...');
+ } finally {
+ $this->datacenter->new_outgoing = [];
+ $this->datacenter->new_incoming = [];
}
}
diff --git a/src/danog/MadelineProto/MTProtoTools/CallHandler.php b/src/danog/MadelineProto/MTProtoTools/CallHandler.php
index 1ebf8cb4..0cd23c5c 100644
--- a/src/danog/MadelineProto/MTProtoTools/CallHandler.php
+++ b/src/danog/MadelineProto/MTProtoTools/CallHandler.php
@@ -115,15 +115,15 @@ trait CallHandler
try {
\danog\MadelineProto\Logger::log('Sending object (try number '.$count.' for '.$object.')...');
$int_message_id = $this->send_message($this->tl->serialize_object(['type' => $object], $args), $this->tl->content_related($object));
- $this->datacenter->outgoing_messages[$int_message_id]['content'] = ['object' => $object, 'args' => $args];
+ $this->datacenter->outgoing_messages[$int_message_id]['content'] = ['method' => $object, 'args' => $args];
} catch (Exception $e) {
\danog\MadelineProto\Logger::log('An error occurred while calling object '.$object.': '.$e->getMessage().' in '.$e->getFile().':'.$e->getLine().'. Recreating connection and retrying to call object...');
$this->datacenter->close_and_reopen();
continue;
}
- return;
+ return $int_message_id;
}
- throw new \danog\MadelineProto\Exception('An error occurred while calling object '.$object.'.');
+ throw new \danog\MadelineProto\Exception('An error occurred while sending object '.$object.'.');
}
}
diff --git a/src/danog/MadelineProto/MTProtoTools/MessageHandler.php b/src/danog/MadelineProto/MTProtoTools/MessageHandler.php
index 5dfd280e..f7692f31 100644
--- a/src/danog/MadelineProto/MTProtoTools/MessageHandler.php
+++ b/src/danog/MadelineProto/MTProtoTools/MessageHandler.php
@@ -123,6 +123,7 @@ trait MessageHandler
}
$deserialized = $this->tl->deserialize($this->fopen_and_write('php://memory', 'rw+b', $message_data));
$this->datacenter->incoming_messages[$message_id]['content'] = $deserialized;
+ $this->datacenter->incoming_messages[$message_id]['response'] = -1;
$this->datacenter->new_incoming[$message_id] = $message_id;
}
}
diff --git a/src/danog/MadelineProto/MTProtoTools/ResponseHandler.php b/src/danog/MadelineProto/MTProtoTools/ResponseHandler.php
index 9cbcec0e..95b5d8f4 100644
--- a/src/danog/MadelineProto/MTProtoTools/ResponseHandler.php
+++ b/src/danog/MadelineProto/MTProtoTools/ResponseHandler.php
@@ -17,10 +17,58 @@ namespace danog\MadelineProto\MTProtoTools;
*/
trait ResponseHandler
{
+
+ private $bad_msg_error_codes = [
+ 16 => 'msg_id too low (most likely, client time is wrong; it would be worthwhile to synchronize it using msg_id notifications and re-send the original message with the “correct” msg_id or wrap it in a container with a new msg_id if the original message had waited too long on the client to be transmitted)',
+ 17 => 'msg_id too high (similar to the previous case, the client time has to be synchronized, and the message re-sent with the correct msg_id)',
+ 18 => 'incorrect two lower order msg_id bits (the server expects client message msg_id to be divisible by 4)',
+ 19 => 'container msg_id is the same as msg_id of a previously received message (this must never happen)',
+ 20 => 'message too old, and it cannot be verified whether the server has received a message with this msg_id or not',
+ 32 => 'msg_seqno too low (the server has already received a message with a lower msg_id but with either a higher or an equal and odd seqno)',
+ 33 => 'msg_seqno too high (similarly, there is a message with a higher msg_id but with either a lower or an equal and odd seqno)',
+ 34 => 'an even msg_seqno expected (irrelevant message), but odd received',
+ 35 => 'odd msg_seqno expected (relevant message), but even received',
+ 48 => 'incorrect server salt (in this case, the bad_server_salt response is received with the correct salt, and the message is to be re-sent with it)',
+ 64 => 'invalid container.',
+ ];
+ private $msgs_info_flags = [
+ 1 => 'nothing is known about the message (msg_id too low, the other party may have forgotten it)',
+ 2 => 'message not received (msg_id falls within the range of stored identifiers; however, the other party has certainly not received a message like that)',
+ 3 => 'message not received (msg_id too high; however, the other party has certainly not received it yet)',
+ 4 => 'message received (note that this response is also at the same time a receipt acknowledgment)',
+ 8 => ' and message already acknowledged',
+ 16 => ' and message not requiring acknowledgment',
+ 32 => ' and RPC query contained in message being processed or processing already complete',
+ 64 => ' and content-related response to message already generated',
+ 128 => ' and other party knows for a fact that message is already received',
+ ];
+ public function send_msgs_state_info($req_msg_id, $msg_ids) {
+ $info = '';
+ foreach ($msg_ids as $msg_id) {
+ $cur_info = 0;
+ if (!in_array($msg_id, $this->datacenter->incoming_messages)) {
+ if (((int) ((time() + $this->datacenter->time_delta + 30) << 32)) < $msg_id) {
+ $cur_info |= 3;
+ } else if (((int) ((time() + $this->datacenter->time_delta - 300) << 32)) > $msg_id) {
+ $cur_info |= 1;
+ } else {
+ $cur_info |= 2;
+ }
+ } else {
+ $cur_info |= 4;
+ if ($this->datacenter->incoming_messages[$msg_id]['ack']) {
+ $cur_info |= 8;
+ }
+ }
+ $info .= chr($cur_info);
+ }
+ $this->datacenter->outgoing_messages[$this->object_call('msgs_state_info', ['req_msg_id' => $req_msg_id, 'info' => $info])]['response'] = $req_msg_id;
+ }
public function handle_messages()
{
foreach ($this->datacenter->new_incoming as $current_msg_id) {
$response = $this->datacenter->incoming_messages[$current_msg_id]['content'];
+ \danog\MadelineProto\Logger::log('Received '.$response['_'].'.');
switch ($response['_']) {
case 'msgs_ack':
foreach ($response['msg_ids'] as $msg_id) {
@@ -66,7 +114,7 @@ trait ResponseHandler
unset($this->datacenter->new_incoming[$current_msg_id]);
break;
case 'msg_container':
- \danog\MadelineProto\Logger::log('Received container.');
+
\danog\MadelineProto\Logger::log($response['messages']);
unset($this->datacenter->new_incoming[$current_msg_id]);
foreach ($response['messages'] as $message) {
@@ -91,24 +139,84 @@ trait ResponseHandler
unset($this->datacenter->new_incoming[$current_msg_id]);
break;
case 'http_wait':
- \danog\MadelineProto\Logger::log('Received http wait.');
+
\danog\MadelineProto\Logger::log($response);
unset($this->datacenter->new_incoming[$current_msg_id]);
break;
- case 'rpc_answer_dropped_running':
- case 'rpc_answer_dropped':
- $this->ack_outgoing_message_id($response['req_msg_id']); // Acknowledge that the server received the original query (the same one, the response to which we wish to forget)
+ case 'msgs_state_info':
+ $this->datacenter->outgoing_messages[$response['req_msg_id']]['response'] = $current_msg_id;
+ unset($this->datacenter->new_incoming[$current_msg_id]);
+ unset($this->datacenter->new_outgoing[$response['req_msg_id']]);
+ break;
+ case 'msgs_state_req':
+ unset($this->datacenter->new_incoming[$current_msg_id]);
+ $this->send_msgs_state_info($current_msg_id, $response['msg_ids']);
+ break;
+ case 'msgs_all_info':
+ unset($this->datacenter->new_incoming[$current_msg_id]);
+
+ foreach ($response['msg_ids'] as $key => $msg_id) {
+ $status = 'Status for message id '.$msg_id.': ';
+ if (($response['info'][$key] & 4) == 1) {
+ $this->ack_outgoing_message_id($msg_id);
+ }
+ foreach ($msgs_info_flags as $flag => $description) {
+ if (($response['info'][$key] & $flag) == 1) {
+ $status .= $description;
+ }
+ }
+ \danog\MadelineProto\Logger::log($status);
+ }
+ break;
+ case 'msg_new_detailed_info':
+ case 'msg_detailed_info':
+
+ if (isset($this->datacenter->incoming_messages[$response['answer_msg_id']])) {
+ $this->ack_incoming_message_id($response['answer_msg_id']);
+ } else {
+ $this->object_call('msg_resend_req', ['msg_ids' => [$response['answer_msg_id']]]);
+ }
+ break;
+ case 'msg_resend_req':
+ $ok = true;
+ unset($this->datacenter->new_incoming[$current_msg_id]);
+ foreach ($response['msg_ids'] as $msg_id) {
+ if (!isset($this->datacenter->outgoing_messages[$msg_id]) || isset($this->datacenter->incoming_messages[$msg_id])) {
+ $ok = false;
+ }
+ }
+ if ($ok) {
+ foreach ($response['msg_ids'] as $msg_id) {
+ $this->object_call($this->datacenter->outgoing_messages[$msg_id]['content']['method'], $this->datacenter->outgoing_messages[$msg_id]['content']['args']);
+ }
+ } else {
+ $this->send_msgs_state_info($current_msg_id, $response['msg_ids']);
+ }
+ break;
+ case 'msg_resend_ans_req':
+ unset($this->datacenter->new_incoming[$current_msg_id]);
+ $this->send_msgs_state_info($response['msg_ids']);
+ foreach ($response['msg_ids'] as $msg_id) {
+ if (isset($this->datacenter->incoming_messages[$msg_id]) && isset($this->datacenter->outgoing_messages[$this->datacenter->incoming_messages[$msg_id]['response']])) {
+ $this->object_call($this->datacenter->outgoing_messages[$this->datacenter->incoming_messages[$msg_id]['response']]['method'], $this->datacenter->outgoing_messages[$this->datacenter->incoming_messages[$msg_id]['response']]['args']);
+ }
+ }
+ break;
default:
$this->ack_incoming_message_id($current_msg_id); // Acknowledge that I received the server's response
$response_type = $this->tl->constructors->find_by_predicate($response['_'])['type'];
+ \danog\MadelineProto\Logger::log('Trying to assign a response of type '.$response_type.' to its request...');
foreach ($this->datacenter->new_outgoing as $key => $expecting) {
+ \danog\MadelineProto\Logger::log('Does the request of return type '.$expecting['type'].' and msg_id '.$expecting['msg_id'].' match?');
if ($response_type == $expecting['type']) {
+ \danog\MadelineProto\Logger::log('Yes');
$this->datacenter->outgoing_messages[$expecting['msg_id']]['response'] = $current_msg_id;
unset($this->datacenter->new_outgoing[$key]);
unset($this->datacenter->new_incoming[$current_msg_id]);
return;
}
+ \danog\MadelineProto\Logger::log('No');
}
throw new \danog\MadelineProto\ResponseException('Dunno how to handle '.PHP_EOL.var_export($response, true));
break;