Fixed handling of all service messages exactly as described in the API (closes #20), fixed bug in authorization module, added hover logo.
This commit is contained in:
parent
1cd457fdcd
commit
7e9383981c
@ -4,9 +4,9 @@
|
|||||||
|
|
||||||
Created by [Daniil Gentili](https://daniil.it), licensed under AGPLv3.
|
Created by [Daniil Gentili](https://daniil.it), licensed under AGPLv3.
|
||||||
|
|
||||||
![MadelineProto logo](https://daniil.it/MadelineProto/logo.png)
|
<img src='https://daniil.it/logo.png' alt='MadelineProto logo' onmouseover="this.src='https://daniil.it/logo-hover.png';" onmouseout="this.src='https://daniil.it/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).
|
PHP implementation of MTProto, based on [telepy](https://github.com/griganton/telepy_old).
|
||||||
|
|
||||||
|
@ -8,9 +8,9 @@ description: PHP implementation of telegram's MTProto protocol
|
|||||||
|
|
||||||
Created by [Daniil Gentili](https://daniil.it), licensed under AGPLv3.
|
Created by [Daniil Gentili](https://daniil.it), licensed under AGPLv3.
|
||||||
|
|
||||||
![MadelineProto logo](https://daniil.it/MadelineProto/logo.png)
|
<img src='https://daniil.it/logo.png' alt='MadelineProto logo' onmouseover="this.src='https://daniil.it/logo-hover.png';" onmouseout="this.src='https://daniil.it/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).
|
PHP implementation of MTProto, based on [telepy](https://github.com/griganton/telepy_old).
|
||||||
|
|
||||||
|
@ -28,23 +28,8 @@ class MTProto extends PrimeModule
|
|||||||
use \danog\MadelineProto\MTProtoTools\SeqNoHandler;
|
use \danog\MadelineProto\MTProtoTools\SeqNoHandler;
|
||||||
|
|
||||||
public $settings = [];
|
public $settings = [];
|
||||||
public $authorized = false;
|
|
||||||
public $waiting_code = false;
|
|
||||||
public $config = ['expires' => -1];
|
public $config = ['expires' => -1];
|
||||||
public $ipv6 = false;
|
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 = [])
|
public function __construct($settings = [])
|
||||||
{
|
{
|
||||||
@ -238,6 +223,10 @@ Slv8kg9qv1m6XHVQY3PnEw+QQtqSIXklHwIDAQAB
|
|||||||
\danog\MadelineProto\Logger::log('Resetting session id and seq_no in DC '.$id.'...');
|
\danog\MadelineProto\Logger::log('Resetting session id and seq_no in DC '.$id.'...');
|
||||||
$socket->session_id = \phpseclib\Crypt\Random::string(8);
|
$socket->session_id = \phpseclib\Crypt\Random::string(8);
|
||||||
$socket->seq_no = 0;
|
$socket->seq_no = 0;
|
||||||
|
$socket->incoming_messages = [];
|
||||||
|
$socket->outgoing_messages = [];
|
||||||
|
$socket->new_outgoing = [];
|
||||||
|
$socket->new_incoming = [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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.')...');
|
\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) {
|
} 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.')...');
|
\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 = [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -115,15 +115,15 @@ trait CallHandler
|
|||||||
try {
|
try {
|
||||||
\danog\MadelineProto\Logger::log('Sending object (try number '.$count.' for '.$object.')...');
|
\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));
|
$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) {
|
} 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...');
|
\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();
|
$this->datacenter->close_and_reopen();
|
||||||
continue;
|
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.'.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -123,6 +123,7 @@ trait MessageHandler
|
|||||||
}
|
}
|
||||||
$deserialized = $this->tl->deserialize($this->fopen_and_write('php://memory', 'rw+b', $message_data));
|
$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]['content'] = $deserialized;
|
||||||
|
$this->datacenter->incoming_messages[$message_id]['response'] = -1;
|
||||||
$this->datacenter->new_incoming[$message_id] = $message_id;
|
$this->datacenter->new_incoming[$message_id] = $message_id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,10 +17,58 @@ namespace danog\MadelineProto\MTProtoTools;
|
|||||||
*/
|
*/
|
||||||
trait ResponseHandler
|
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()
|
public function handle_messages()
|
||||||
{
|
{
|
||||||
foreach ($this->datacenter->new_incoming as $current_msg_id) {
|
foreach ($this->datacenter->new_incoming as $current_msg_id) {
|
||||||
$response = $this->datacenter->incoming_messages[$current_msg_id]['content'];
|
$response = $this->datacenter->incoming_messages[$current_msg_id]['content'];
|
||||||
|
\danog\MadelineProto\Logger::log('Received '.$response['_'].'.');
|
||||||
switch ($response['_']) {
|
switch ($response['_']) {
|
||||||
case 'msgs_ack':
|
case 'msgs_ack':
|
||||||
foreach ($response['msg_ids'] as $msg_id) {
|
foreach ($response['msg_ids'] as $msg_id) {
|
||||||
@ -66,7 +114,7 @@ trait ResponseHandler
|
|||||||
unset($this->datacenter->new_incoming[$current_msg_id]);
|
unset($this->datacenter->new_incoming[$current_msg_id]);
|
||||||
break;
|
break;
|
||||||
case 'msg_container':
|
case 'msg_container':
|
||||||
\danog\MadelineProto\Logger::log('Received container.');
|
|
||||||
\danog\MadelineProto\Logger::log($response['messages']);
|
\danog\MadelineProto\Logger::log($response['messages']);
|
||||||
unset($this->datacenter->new_incoming[$current_msg_id]);
|
unset($this->datacenter->new_incoming[$current_msg_id]);
|
||||||
foreach ($response['messages'] as $message) {
|
foreach ($response['messages'] as $message) {
|
||||||
@ -91,24 +139,84 @@ trait ResponseHandler
|
|||||||
unset($this->datacenter->new_incoming[$current_msg_id]);
|
unset($this->datacenter->new_incoming[$current_msg_id]);
|
||||||
break;
|
break;
|
||||||
case 'http_wait':
|
case 'http_wait':
|
||||||
\danog\MadelineProto\Logger::log('Received http wait.');
|
|
||||||
\danog\MadelineProto\Logger::log($response);
|
\danog\MadelineProto\Logger::log($response);
|
||||||
unset($this->datacenter->new_incoming[$current_msg_id]);
|
unset($this->datacenter->new_incoming[$current_msg_id]);
|
||||||
break;
|
break;
|
||||||
case 'rpc_answer_dropped_running':
|
case 'msgs_state_info':
|
||||||
case 'rpc_answer_dropped':
|
$this->datacenter->outgoing_messages[$response['req_msg_id']]['response'] = $current_msg_id;
|
||||||
$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)
|
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:
|
default:
|
||||||
$this->ack_incoming_message_id($current_msg_id); // Acknowledge that I received the server's response
|
$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'];
|
$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) {
|
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']) {
|
if ($response_type == $expecting['type']) {
|
||||||
|
\danog\MadelineProto\Logger::log('Yes');
|
||||||
$this->datacenter->outgoing_messages[$expecting['msg_id']]['response'] = $current_msg_id;
|
$this->datacenter->outgoing_messages[$expecting['msg_id']]['response'] = $current_msg_id;
|
||||||
unset($this->datacenter->new_outgoing[$key]);
|
unset($this->datacenter->new_outgoing[$key]);
|
||||||
unset($this->datacenter->new_incoming[$current_msg_id]);
|
unset($this->datacenter->new_incoming[$current_msg_id]);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
\danog\MadelineProto\Logger::log('No');
|
||||||
}
|
}
|
||||||
throw new \danog\MadelineProto\ResponseException('Dunno how to handle '.PHP_EOL.var_export($response, true));
|
throw new \danog\MadelineProto\ResponseException('Dunno how to handle '.PHP_EOL.var_export($response, true));
|
||||||
break;
|
break;
|
||||||
|
Loading…
Reference in New Issue
Block a user