Rewrote TL class using webogram, fixed bug in recognition of system version, improved response handling, improved tests, improved documentation
This commit is contained in:
parent
1d9ea6ad0c
commit
8bba87c353
@ -16,5 +16,7 @@ before_script:
|
||||
script:
|
||||
- "./testing.php"
|
||||
before_install:
|
||||
- openssl aes-256-cbc -K $encrypted_f108bed02ef2_key -iv $encrypted_f108bed02ef2_iv
|
||||
- openssl aes-256-cbc -K $encrypted_0b05103af24b_key -iv $encrypted_0b05103af24b_iv
|
||||
-in session.madeline.enc -out session.madeline -d
|
||||
- openssl aes-256-cbc -K $encrypted_0b05103af24b_key -iv $encrypted_0b05103af24b_iv
|
||||
-in token.php.enc -out token.php -d
|
||||
|
@ -2,4 +2,6 @@
|
||||
|
||||
To contribute to this project simply fork it, make all improvements, commit changes (squashing is recommended for multiple commits) and submit a pull request. That's it!
|
||||
|
||||
If you have an issue, please *always* upload MadelineProto logs from when the error occurred to hastebin or similar websites.
|
||||
|
||||
Bye :)
|
||||
|
@ -181,6 +181,12 @@ $checkedPhone = $MadelineProto->auth->checkPhone( // auth.checkPhone becomes aut
|
||||
]
|
||||
);
|
||||
$ping = $MadelineProto->ping([3]); // parameter names can be omitted as long as the order specified by the TL scheme is respected
|
||||
$message = "Hey! I'm sending this message with MadelineProto!";
|
||||
$username = $MadelineProto->contacts->resolveUsername(['username' => 'pwrtelegramgroup']);
|
||||
var_dump($username);
|
||||
$peer = ['_' => 'inputPeerChannel', 'channel_id' => $username['peer']['channel_id'], 'access_hash' => $username['chats'][0]['access_hash']];
|
||||
$sentMessage = $MadelineProto->messages->sendMessage(['peer' => $peer, 'message' => $message, 'random_id' => \danog\PHP\Struct::unpack('<q', \phpseclib\Crypt\Random::string(8))[0]]);
|
||||
var_dump($sentMessage);
|
||||
```
|
||||
|
||||
The API class also provides some wrapper methods for logging in as a bot or as a normal user:
|
||||
@ -199,6 +205,8 @@ $authorization = $MadelineProto->bot_login($token); // Note that every time you
|
||||
var_dump($authorization);
|
||||
```
|
||||
|
||||
See testing.php for more examples.
|
||||
|
||||
### Storing sessions
|
||||
|
||||
An istance of MadelineProto can be safely serialized or unserialized.
|
||||
|
@ -25,6 +25,7 @@ class MTProto extends MTProtoTools
|
||||
|
||||
public function __construct($settings = [])
|
||||
{
|
||||
// Detect ipv6
|
||||
$google = '';
|
||||
try {
|
||||
$google = file_get_contents('https://ipv6.google.com');
|
||||
@ -32,6 +33,22 @@ class MTProto extends MTProtoTools
|
||||
}
|
||||
$this->ipv6 = strlen($google) > 0;
|
||||
|
||||
|
||||
// Detect device model
|
||||
$device_model = 'Web server';
|
||||
try {
|
||||
$device_model = php_uname('s');
|
||||
} catch (Exception $e) {
|
||||
}
|
||||
|
||||
|
||||
// Detect system version
|
||||
$system_version = phpversion();
|
||||
try {
|
||||
$system_version = php_uname('r');
|
||||
} catch (Exception $e) {
|
||||
}
|
||||
|
||||
// Set default settings
|
||||
$default_settings = [
|
||||
'authorization' => [ // Authorization settings
|
||||
@ -101,8 +118,8 @@ Slv8kg9qv1m6XHVQY3PnEw+QQtqSIXklHwIDAQAB
|
||||
'app_info' => [ // obtained in https://my.telegram.org
|
||||
'api_id' => 25628,
|
||||
'api_hash' => '1fe17cda7d355166cdaa71f04122873c',
|
||||
'device_model' => php_uname('s'),
|
||||
'system_version' => php_uname('r'),
|
||||
'device_model' => $device_model,
|
||||
'system_version' => $system_version,
|
||||
'app_version' => 'Unicorn', // 🌚
|
||||
'lang_code' => 'en',
|
||||
],
|
||||
|
@ -21,9 +21,10 @@ class AckHandler extends \danog\MadelineProto\PrimeModule
|
||||
{
|
||||
// The server acknowledges that it received my message
|
||||
if (!isset($this->datacenter->outgoing_messages[$message_id])) {
|
||||
throw new \danog\MadelineProto\Exception("Couldn't find message id ".$message_id.' in the array of outgoing messages. Maybe try to increase its size?');
|
||||
\danog\MadelineProto\Logger::log("Couldn't find message id ".$message_id.' in the array of outgoing messages. Maybe try to increase its size?');
|
||||
return false;
|
||||
}
|
||||
$this->datacenter->outgoing_messages[$message_id]['ack'] = true;
|
||||
return $this->datacenter->outgoing_messages[$message_id]['ack'] = true;
|
||||
}
|
||||
|
||||
public function ack_incoming_message_id($message_id)
|
||||
@ -37,6 +38,6 @@ class AckHandler extends \danog\MadelineProto\PrimeModule
|
||||
}
|
||||
|
||||
$this->object_call('msgs_ack', ['msg_ids' => [$message_id]]);
|
||||
$this->datacenter->incoming_messages[$message_id]['ack'] = true;
|
||||
return $this->datacenter->incoming_messages[$message_id]['ack'] = true;
|
||||
}
|
||||
}
|
||||
|
@ -64,7 +64,6 @@ class AuthKeyHandler extends AckHandler
|
||||
*/
|
||||
foreach ($ResPQ['server_public_key_fingerprints'] as $curfp) {
|
||||
$curfp_biginteger = new \phpseclib\Math\BigInteger($curfp);
|
||||
|
||||
if ($this->key->fp->equals($curfp_biginteger)) {
|
||||
$public_key_fingerprint = $curfp;
|
||||
break;
|
||||
@ -115,7 +114,7 @@ class AuthKeyHandler extends AckHandler
|
||||
'new_nonce' => $new_nonce,
|
||||
'expires_in' => $expires_in,
|
||||
];
|
||||
$p_q_inner_data = $this->tl->serialize_obj('p_q_inner_data'.(($expires_in < 0) ? '' : '_temp'), $data_unserialized);
|
||||
$p_q_inner_data = $this->tl->serialize_object(['type' => 'p_q_inner_data'.(($expires_in < 0) ? '' : '_temp')], $data_unserialized);
|
||||
|
||||
/*
|
||||
* ***********************************************************************
|
||||
@ -350,8 +349,8 @@ class AuthKeyHandler extends AckHandler
|
||||
* string $g_b : g^b mod dh_prime
|
||||
* ]
|
||||
*/
|
||||
$data = $this->tl->serialize_obj(
|
||||
'client_DH_inner_data',
|
||||
$data = $this->tl->serialize_object(
|
||||
['type' => 'client_DH_inner_data'],
|
||||
[
|
||||
'nonce' => $nonce,
|
||||
'server_nonce' => $server_nonce,
|
||||
@ -470,9 +469,9 @@ class AuthKeyHandler extends AckHandler
|
||||
}
|
||||
}
|
||||
} catch (\danog\MadelineProto\Exception $e) {
|
||||
\danog\MadelineProto\Logger::log('An exception occurred while generating the authorization key. Retrying...');
|
||||
\danog\MadelineProto\Logger::log('An exception occurred while generating the authorization key: '.$e->getMessage().' Retrying...');
|
||||
} catch (\danog\MadelineProto\RPCErrorException $e) {
|
||||
\danog\MadelineProto\Logger::log('An RPCErrorExceptiom occurred while generating the authorization key. Retrying...');
|
||||
\danog\MadelineProto\Logger::log('An RPCErrorException occurred while generating the authorization key: '.$e->getMessage().' Retrying...');
|
||||
}
|
||||
}
|
||||
|
||||
@ -487,7 +486,7 @@ class AuthKeyHandler extends AckHandler
|
||||
$temp_auth_key_id = \danog\PHP\Struct::unpack('<q', $this->datacenter->temp_auth_key['id'])[0];
|
||||
$perm_auth_key_id = \danog\PHP\Struct::unpack('<q', $this->datacenter->auth_key['id'])[0];
|
||||
$temp_session_id = \danog\PHP\Struct::unpack('<q', $this->datacenter->session_id)[0];
|
||||
$message_data = $this->tl->serialize_obj('bind_auth_key_inner',
|
||||
$message_data = $this->tl->serialize_object(['type' => 'bind_auth_key_inner'],
|
||||
[
|
||||
'nonce' => $nonce,
|
||||
'temp_auth_key_id' => $temp_auth_key_id,
|
||||
|
@ -17,17 +17,14 @@ namespace danog\MadelineProto\MTProtoTools;
|
||||
*/
|
||||
class CallHandler extends AuthKeyHandler
|
||||
{
|
||||
public function wait_for_response($last_sent, $optional_name = null)
|
||||
public function wait_for_response($last_sent, $optional_name, $response_type)
|
||||
{
|
||||
if ($optional_name == null) {
|
||||
$optional_name = $last_sent;
|
||||
}
|
||||
$response = null;
|
||||
$count = 0;
|
||||
while ($response == null && $count++ < $this->settings['max_tries']['response']) {
|
||||
\danog\MadelineProto\Logger::log('Getting response (try number '.$count.' for '.$optional_name.')...');
|
||||
$last_received = $this->recv_message();
|
||||
$this->handle_message($last_sent, $last_received);
|
||||
$this->handle_message($last_sent, $last_received, $response_type);
|
||||
if (isset($this->datacenter->outgoing_messages[$last_sent]['response']) && isset($this->datacenter->incoming_messages[$this->datacenter->outgoing_messages[$last_sent]['response']]['content'])) {
|
||||
$response = $this->datacenter->incoming_messages[$this->datacenter->outgoing_messages[$last_sent]['response']]['content'];
|
||||
}
|
||||
@ -67,7 +64,7 @@ class CallHandler extends AuthKeyHandler
|
||||
$args = $this->tl->get_named_method_args($method, $args);
|
||||
$int_message_id = $this->send_message($this->tl->serialize_method($method, $args), $this->tl->content_related($method), $message_id);
|
||||
$this->datacenter->outgoing_messages[$int_message_id]['content'] = ['method' => $method, 'args' => $args];
|
||||
$server_answer = $this->wait_for_response($int_message_id, $method);
|
||||
$server_answer = $this->wait_for_response($int_message_id, $method, $this->tl->methods->find_by_method($method)['type']);
|
||||
} catch (\danog\MadelineProto\Exception $e) {
|
||||
\danog\MadelineProto\Logger::log('An error occurred while calling method '.$method.': '.$e->getMessage().' in '.basename($e->getFile(), '.php').' on line '.$e->getLine().'. Recreating connection and retrying to call method...');
|
||||
$this->datacenter->close_and_reopen();
|
||||
@ -91,7 +88,7 @@ class CallHandler extends AuthKeyHandler
|
||||
foreach (range(1, $this->settings['max_tries']['query']) as $count) {
|
||||
try {
|
||||
\danog\MadelineProto\Logger::log('Sending object (try number '.$count.' for '.$object.')...');
|
||||
$int_message_id = $this->send_message($this->tl->serialize_obj($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];
|
||||
// $server_answer = $this->wait_for_response($int_message_id);
|
||||
} catch (Exception $e) {
|
||||
|
@ -54,7 +54,8 @@ class MessageHandler extends Crypt
|
||||
{
|
||||
$payload = $this->datacenter->read_message();
|
||||
if (fstat($payload)['size'] == 4) {
|
||||
throw new \danog\MadelineProto\RPCErrorException('Generic', \danog\PHP\Struct::unpack('<i', fread($payload, 4))[0]);
|
||||
$error = \danog\PHP\Struct::unpack('<i', fread($payload, 4))[0];
|
||||
throw new \danog\MadelineProto\RPCErrorException($error, $error);
|
||||
}
|
||||
$auth_key_id = fread($payload, 8);
|
||||
if ($auth_key_id == $this->string2bin('\x00\x00\x00\x00\x00\x00\x00\x00')) {
|
||||
|
@ -19,10 +19,10 @@ class MsgIdHandler extends MessageHandler
|
||||
{
|
||||
public function check_message_id($new_message_id, $outgoing, $container = false)
|
||||
{
|
||||
if (((int) ((time() + $this->datacenter->time_delta - 300) * pow(2, 30)) * 4) > $new_message_id) {
|
||||
if (((int) ((time() + $this->datacenter->time_delta - 300) << 32)) > $new_message_id) {
|
||||
throw new \danog\MadelineProto\Exception('Given message id ('.$new_message_id.') is too old.');
|
||||
}
|
||||
if (((int) ((time() + $this->datacenter->time_delta + 30) * pow(2, 30)) * 4) < $new_message_id) {
|
||||
if (((int) ((time() + $this->datacenter->time_delta + 30) << 32)) < $new_message_id) {
|
||||
throw new \danog\MadelineProto\Exception('Given message id ('.$new_message_id.') is too new.');
|
||||
}
|
||||
if ($outgoing) {
|
||||
|
@ -17,7 +17,7 @@ namespace danog\MadelineProto\MTProtoTools;
|
||||
*/
|
||||
class ResponseHandler extends MsgIdHandler
|
||||
{
|
||||
public function handle_message($last_sent, $last_received)
|
||||
public function handle_message($last_sent, $last_received, $expected_type)
|
||||
{
|
||||
$response = $this->datacenter->incoming_messages[$last_received]['content'];
|
||||
|
||||
@ -30,17 +30,11 @@ class ResponseHandler extends MsgIdHandler
|
||||
|
||||
case 'rpc_result':
|
||||
$this->ack_incoming_message_id($last_received); // Acknowledge that I received the server's response
|
||||
$this->ack_outgoing_message_id($response['req_msg_id']); // Acknowledge that the server received my request
|
||||
$this->datacenter->outgoing_messages[$response['req_msg_id']]['response'] = $last_received;
|
||||
$this->datacenter->incoming_messages[$last_received]['content'] = $response['result'];
|
||||
|
||||
return $this->handle_message($last_sent, $last_received);
|
||||
break;
|
||||
|
||||
case 'future_salts':
|
||||
$this->ack_outgoing_message_id($response['req_msg_id']); // Acknowledge that the server received my request
|
||||
$this->datacenter->outgoing_messages[$response['req_msg_id']]['response'] = $last_received;
|
||||
$this->datacenter->incoming_messages[$last_received]['content'] = $response;
|
||||
|
||||
$this->try_store_response($response['req_msg_id'], $last_received, $expected_type, true);
|
||||
break;
|
||||
|
||||
case 'bad_server_salt':
|
||||
@ -67,7 +61,6 @@ class ResponseHandler extends MsgIdHandler
|
||||
}
|
||||
throw new \danog\MadelineProto\RPCErrorException('Received bad_msg_notification for '.$response['bad_msg_id'].': '.$error_codes[$response['error_code']]);
|
||||
break;
|
||||
break;
|
||||
|
||||
case 'pong':
|
||||
foreach ($this->datacenter->outgoing_messages as $msg_id => &$omessage) {
|
||||
@ -91,7 +84,7 @@ class ResponseHandler extends MsgIdHandler
|
||||
foreach ($response['messages'] as $message) {
|
||||
$this->check_message_id($message['msg_id'], false, true);
|
||||
$this->datacenter->incoming_messages[$message['msg_id']] = ['seq_no' => $message['seqno'], 'content' => $message['body']];
|
||||
$responses[] = $this->handle_message($last_sent, $message['msg_id']);
|
||||
$responses[] = $this->handle_message($last_sent, $message['msg_id'], $expected_type);
|
||||
}
|
||||
foreach ($responses as $key => $response) {
|
||||
if ($response == null) {
|
||||
@ -121,26 +114,28 @@ class ResponseHandler extends MsgIdHandler
|
||||
$this->check_message_id($message['orig_message']['msg_id'], false, true);
|
||||
$this->datacenter->incoming_messages[$message['orig_message']['msg_id']] = ['content' => $response['orig_message']];
|
||||
|
||||
return $this->handle_message($last_sent, $message['orig_message']['msg_id']);
|
||||
return $this->handle_message($last_sent, $message['orig_message']['msg_id'], $expected_type);
|
||||
}
|
||||
break;
|
||||
case 'http_wait':
|
||||
\danog\MadelineProto\Logger::log('Received http wait.');
|
||||
\danog\MadelineProto\Logger::log($response);
|
||||
break;
|
||||
case 'gzip_packed':
|
||||
$this->datacenter->incoming_messages[$last_received]['content'] = $this->tl->deserialize($this->fopen_and_write('php://memory', 'rw+b', gzdecode($response['packed_data'])));
|
||||
|
||||
return $this->handle_message($last_sent, $last_received);
|
||||
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)
|
||||
default:
|
||||
$this->ack_incoming_message_id($last_received); // Acknowledge that I received the server's response
|
||||
$this->datacenter->outgoing_messages[$last_sent]['response'] = $last_received;
|
||||
$this->datacenter->incoming_messages[$last_received]['content'] = $response;
|
||||
$this->try_store_response($last_sent, $last_received, $expected_type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
public function try_store_response($request, $response, $type, $force = true) {
|
||||
if ($force) {
|
||||
return $this->datacenter->outgoing_messages[$request]['response'] = $response;
|
||||
}
|
||||
if ($this->tl->constructors->find_by_predicate($this->datacenter->incoming_messages[$response]['content']['_'])['type'] == $type) {
|
||||
$this->datacenter->outgoing_messages[$request]['response'] = $response;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -28,20 +28,17 @@ class RSA extends TL\TL
|
||||
$key->loadKey($rsa_key);
|
||||
$this->n = $key->modulus;
|
||||
$this->e = $key->exponent;
|
||||
unset($key);
|
||||
|
||||
\danog\MadelineProto\Logger::log('Computing fingerprint...');
|
||||
$this->fp_bytes = substr(
|
||||
sha1(
|
||||
$this->serialize_param(
|
||||
'bytes',
|
||||
null,
|
||||
$this->serialize_object(
|
||||
['type' => 'bytes'],
|
||||
$this->n->toBytes()
|
||||
)
|
||||
.
|
||||
$this->serialize_param(
|
||||
'bytes',
|
||||
null,
|
||||
$this->serialize_object(
|
||||
['type' => 'bytes'],
|
||||
$this->e->toBytes()
|
||||
),
|
||||
true
|
||||
@ -55,6 +52,7 @@ class RSA extends TL\TL
|
||||
|
||||
public function encrypt($data)
|
||||
{
|
||||
\danog\MadelineProto\Logger::log('Encrypting with rsa key...');
|
||||
$bigintdata = new \phpseclib\Math\BigInteger($data, 256);
|
||||
|
||||
return $bigintdata->powMod($this->e, $this->n)->toBytes();
|
||||
|
@ -54,29 +54,122 @@ class TL extends \danog\MadelineProto\Tools
|
||||
return $arguments;
|
||||
}
|
||||
|
||||
public function serialize_obj($object, $arguments)
|
||||
public function serialize_bool($bool) {
|
||||
return \danog\PHP\Struct::pack('<i', $this->constructors->find_by_predicate('bool'.($bool ? 'True' : 'False'))['id']);
|
||||
}
|
||||
public function deserialize_bool($data) {
|
||||
$id = \danog\PHP\Struct::unpack('<i', $data) [0];
|
||||
$tl_elem = $this->constructors->find_by_id($id);
|
||||
if ($tl_elem === false) {
|
||||
throw new Exception('Could not extract boolean');
|
||||
}
|
||||
return $tl_elem['predicate'] === 'boolTrue';
|
||||
}
|
||||
|
||||
public function serialize_object($type, $object)
|
||||
{
|
||||
$tl_constructor = $this->constructors->find_by_predicate($object);
|
||||
if ($tl_constructor === false) {
|
||||
switch ($type['type']) {
|
||||
case 'int':
|
||||
if (!is_numeric($object)) {
|
||||
throw new Exception("given value isn't numeric");
|
||||
}
|
||||
|
||||
return \danog\PHP\Struct::pack('<i', $object);
|
||||
case '#':
|
||||
if (!is_numeric($object)) {
|
||||
throw new Exception("given value isn't numeric");
|
||||
}
|
||||
|
||||
return \danog\PHP\Struct::pack('<I', $object);
|
||||
case 'long':
|
||||
if (!is_numeric($object)) {
|
||||
var_dump($object);
|
||||
throw new Exception("given value isn't numeric");
|
||||
}
|
||||
|
||||
return \danog\PHP\Struct::pack('<q', $object);
|
||||
case 'int128':
|
||||
case 'int256':
|
||||
case 'int512':
|
||||
return (string) $object;
|
||||
case 'double':
|
||||
return \danog\PHP\Struct::pack('<d', $object);
|
||||
case 'string':
|
||||
case 'bytes':
|
||||
$l = strlen($object);
|
||||
$concat = '';
|
||||
if ($l <= 253) {
|
||||
$concat .= \danog\PHP\Struct::pack('<b', $l);
|
||||
$concat .= $object;
|
||||
$concat .= pack('@'.$this->posmod((-$l - 1), 4));
|
||||
} else {
|
||||
$concat .= $this->string2bin('\xfe');
|
||||
$concat .= substr(\danog\PHP\Struct::pack('<i', $l), 0, 3);
|
||||
$concat .= $object;
|
||||
$concat .= pack('@'.$this->posmod(-$l, 4));
|
||||
}
|
||||
return $concat;
|
||||
case 'Bool':
|
||||
if (!is_bool($object)) {
|
||||
throw new Exception("given value isn't a boolean");
|
||||
}
|
||||
|
||||
return $this->serialize_bool($object);
|
||||
case 'true':
|
||||
return;
|
||||
case '!X':
|
||||
return $object;
|
||||
case 'Vector t':
|
||||
$concat = \danog\PHP\Struct::pack('<i', $this->constructors->find_by_predicate('vector')['id']);
|
||||
$concat .= \danog\PHP\Struct::pack('<i', count($object));
|
||||
foreach ($object as $current_object) {
|
||||
$concat .= $this->serialize_object(['type' => $type['subtype']], $current_object);
|
||||
}
|
||||
return $concat;
|
||||
|
||||
}
|
||||
$auto = false;
|
||||
if (!isset($object['_'])) {
|
||||
$constructorData = $this->constructors->find_by_predicate($type['type']);
|
||||
if ($constructorData === false) {
|
||||
throw new Exception('Predicate was not set!');
|
||||
}
|
||||
$auto = true;
|
||||
$object['_'] = $constructorData['predicate'];
|
||||
}
|
||||
$predicate = $object['_'];
|
||||
|
||||
$constructorData = $this->constructors->find_by_predicate($predicate);
|
||||
if ($constructorData === false) {
|
||||
throw new Exception('Could not extract type: '.$object);
|
||||
}
|
||||
|
||||
return $this->serialize_generic($tl_constructor, $arguments);
|
||||
}
|
||||
|
||||
public function serialize_method($method, $arguments)
|
||||
{
|
||||
$tl_method = $this->methods->find_by_method($method);
|
||||
if ($tl_method === false) {
|
||||
throw new Exception('Could not extract type: '.$method);
|
||||
if ($bare = ($type['type'] != '' && $type['type'][0] == "%")) {
|
||||
$type['type'] = substr($type['type'], 1);
|
||||
}
|
||||
|
||||
return $this->serialize_generic($tl_method, $arguments);
|
||||
}
|
||||
if ($predicate == $type['type'] && !$auto) {
|
||||
$bare = true;
|
||||
}
|
||||
|
||||
public function serialize_generic($tl, $arguments)
|
||||
$concat = '';
|
||||
if (!$bare) {
|
||||
$concat .= \danog\PHP\Struct::pack('<i', $constructorData['id']);
|
||||
}
|
||||
return $concat.$this->serialize_params($constructorData, $object);
|
||||
|
||||
}
|
||||
public function serialize_method($method, $arguments)
|
||||
{
|
||||
$serialized = \danog\PHP\Struct::pack('<i', $tl['id']);
|
||||
$tl = $this->methods->find_by_method($method);
|
||||
if ($tl === false) {
|
||||
throw new Exception('Could not extract type: '.$method);
|
||||
}
|
||||
return \danog\PHP\Struct::pack('<i', $tl['id']).$this->serialize_params($tl, $arguments);
|
||||
}
|
||||
public function serialize_params($tl, $arguments)
|
||||
{
|
||||
$serialized = '';
|
||||
$flags = 0;
|
||||
foreach ($tl['params'] as $cur_flag) {
|
||||
if ($cur_flag['flag']) {
|
||||
@ -108,87 +201,15 @@ class TL extends \danog\MadelineProto\Tools
|
||||
throw new Exception('Missing required parameter ('.$current_argument['name'].')');
|
||||
}
|
||||
//\danog\MadelineProto\Logger::log('Serializing '.$current_argument['name'].' of type '.$current_argument['type'].'/'.$current_argument['subtype']);
|
||||
$serialized .= $this->serialize_param($current_argument['type'], $current_argument['subtype'], $arguments[$current_argument['name']]);
|
||||
$serialized .= $this->serialize_object($current_argument, $arguments[$current_argument['name']]);
|
||||
}
|
||||
|
||||
return $serialized;
|
||||
}
|
||||
|
||||
public function serialize_param($type, $subtype = null, $value = null)
|
||||
public function get_length($bytes_io, $type = ['type' => ''])
|
||||
{
|
||||
switch ($type) {
|
||||
case 'Bool':
|
||||
if (!is_bool($value)) {
|
||||
throw new Exception("serialize_param: given value isn't a boolean");
|
||||
}
|
||||
|
||||
return $this->serialize_param('bool'.($value ? 'True' : 'False'));
|
||||
break;
|
||||
case 'int':
|
||||
if (!is_numeric($value)) {
|
||||
throw new Exception("serialize_param: given value isn't numeric");
|
||||
}
|
||||
|
||||
return \danog\PHP\Struct::pack('<i', $value);
|
||||
break;
|
||||
case '#':
|
||||
if (!is_numeric($value)) {
|
||||
throw new Exception("serialize_param: given value isn't numeric");
|
||||
}
|
||||
|
||||
return \danog\PHP\Struct::pack('<I', $value);
|
||||
break;
|
||||
case 'long':
|
||||
if (!is_numeric($value)) {
|
||||
throw new Exception("serialize_param: given value isn't numeric");
|
||||
}
|
||||
|
||||
return \danog\PHP\Struct::pack('<q', $value);
|
||||
break;
|
||||
case 'int128':
|
||||
case 'int256':
|
||||
return (string) $value;
|
||||
break;
|
||||
case 'double':
|
||||
return \danog\PHP\Struct::pack('<d', $value);
|
||||
break;
|
||||
case 'string':
|
||||
case 'bytes':
|
||||
$l = strlen($value);
|
||||
$concat = '';
|
||||
if ($l <= 253) {
|
||||
$concat .= \danog\PHP\Struct::pack('<b', $l);
|
||||
$concat .= $value;
|
||||
$concat .= pack('@'.$this->posmod((-$l - 1), 4));
|
||||
} else {
|
||||
$concat .= $this->string2bin('\xfe');
|
||||
$concat .= substr(\danog\PHP\Struct::pack('<i', $l), 0, 3);
|
||||
$concat .= $value;
|
||||
$concat .= pack('@'.$this->posmod(-$l, 4));
|
||||
}
|
||||
|
||||
return $concat;
|
||||
break;
|
||||
case '!X':
|
||||
return $value;
|
||||
case 'Vector t':
|
||||
$concat = \danog\PHP\Struct::pack('<i', $this->constructors->find_by_predicate('vector')['id']);
|
||||
|
||||
$concat .= \danog\PHP\Struct::pack('<l', count($value));
|
||||
foreach ($value as $curv) {
|
||||
$concat .= $this->serialize_param($subtype, null, $curv);
|
||||
}
|
||||
|
||||
return $concat;
|
||||
default:
|
||||
return $this->serialize_generic($type, $value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public function get_length($bytes_io, $type = null, $subtype = null)
|
||||
{
|
||||
$this->deserialize($bytes_io, $type, $subtype);
|
||||
$this->deserialize($bytes_io, $type);
|
||||
|
||||
return ftell($bytes_io);
|
||||
}
|
||||
@ -196,39 +217,33 @@ class TL extends \danog\MadelineProto\Tools
|
||||
/**
|
||||
* :type bytes_io: io.BytesIO object.
|
||||
*/
|
||||
public function deserialize($bytes_io, $type = null, $subtype = null)
|
||||
public function deserialize($bytes_io, $type = ['type' => ''])
|
||||
{
|
||||
if (!(get_resource_type($bytes_io) == 'file' || get_resource_type($bytes_io) == 'stream')) {
|
||||
throw new Exception('An invalid bytes_io handle was provided.');
|
||||
if (!(!is_string($bytes_io) && (get_resource_type($bytes_io) == 'file' || get_resource_type($bytes_io) == 'stream'))) {
|
||||
if (is_string($bytes_io)) {
|
||||
$bytes_io = $this->fopen_and_write('php://memory', 'rw+b', $bytes_io);
|
||||
} else {
|
||||
throw new Exception('An invalid bytes_io handle was provided.');
|
||||
}
|
||||
}
|
||||
//\danog\MadelineProto\Logger::log('Deserializing '.$type.'/'.$subtype.' at byte '.ftell($bytes_io));
|
||||
switch ($type) {
|
||||
//\danog\MadelineProto\Logger::log('Deserializing '.$type['type'].'/'.$subtype.' at byte '.ftell($bytes_io));
|
||||
switch ($type['type']) {
|
||||
case 'Bool':
|
||||
$id = \danog\PHP\Struct::unpack('<i', fread($bytes_io, 4)) [0];
|
||||
$tl_elem = $this->constructors->find_by_id($id);
|
||||
if ($tl_elem === false) {
|
||||
throw new Exception('Could not extract type: '.$type.' with id '.$id);
|
||||
}
|
||||
$x = $tl_elem['predicate'] === 'boolTrue';
|
||||
break;
|
||||
return $this->deserialize_bool(fread($bytes_io, 4));
|
||||
case 'int':
|
||||
$x = \danog\PHP\Struct::unpack('<i', fread($bytes_io, 4)) [0];
|
||||
break;
|
||||
return \danog\PHP\Struct::unpack('<i', fread($bytes_io, 4)) [0];
|
||||
case '#':
|
||||
$x = \danog\PHP\Struct::unpack('<I', fread($bytes_io, 4)) [0];
|
||||
break;
|
||||
return \danog\PHP\Struct::unpack('<I', fread($bytes_io, 4)) [0];
|
||||
case 'long':
|
||||
$x = \danog\PHP\Struct::unpack('<q', fread($bytes_io, 8)) [0];
|
||||
break;
|
||||
return \danog\PHP\Struct::unpack('<q', fread($bytes_io, 8)) [0];
|
||||
case 'double':
|
||||
$x = \danog\PHP\Struct::unpack('<d', fread($bytes_io, 8)) [0];
|
||||
break;
|
||||
return \danog\PHP\Struct::unpack('<d', fread($bytes_io, 8)) [0];
|
||||
case 'int128':
|
||||
$x = fread($bytes_io, 16);
|
||||
break;
|
||||
return fread($bytes_io, 16);
|
||||
case 'int256':
|
||||
$x = fread($bytes_io, 32);
|
||||
break;
|
||||
return fread($bytes_io, 32);
|
||||
case 'int512':
|
||||
return fread($bytes_io, 32);
|
||||
case 'string':
|
||||
case 'bytes':
|
||||
$l = \danog\PHP\Struct::unpack('<B', fread($bytes_io, 1)) [0];
|
||||
@ -252,62 +267,76 @@ class TL extends \danog\MadelineProto\Tools
|
||||
if (!is_string($x)) {
|
||||
throw new Exception("deserialize: generated value isn't a string");
|
||||
}
|
||||
break;
|
||||
return $x;
|
||||
case 'true':
|
||||
return true;
|
||||
case 'Vector t':
|
||||
$id = \danog\PHP\Struct::unpack('<i', fread($bytes_io, 4)) [0];
|
||||
$constructorData = $this->constructors->find_by_id($id);
|
||||
if ($constructorData === false) {
|
||||
throw new Exception('Could not extract type: '.$type['type'].' with id '.$id);
|
||||
}
|
||||
switch ($constructorData['predicate']) {
|
||||
case 'gzip_packed':
|
||||
return $this->deserialize(gzdecode($this->deserialize($bytes_io, ['type' => 'string'])));
|
||||
case 'Vector t':
|
||||
case 'vector':
|
||||
break;
|
||||
default:
|
||||
throw new Exception('Invalid vector constructor: '.$constructorData['predicate']);
|
||||
}
|
||||
case 'vector':
|
||||
if ($subtype == null) {
|
||||
throw new Exception('deserialize: subtype is null');
|
||||
}
|
||||
$count = \danog\PHP\Struct::unpack('<l', fread($bytes_io, 4)) [0];
|
||||
$x = [];
|
||||
$count = \danog\PHP\Struct::unpack('<i', fread($bytes_io, 4)) [0];
|
||||
$result = [];
|
||||
for ($i = 0; $i < $count; $i++) {
|
||||
$x[] = $this->deserialize($bytes_io, $subtype);
|
||||
$result[] = $this->deserialize($bytes_io, ['type' => $type['subtype']]);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
$tl_elem = $this->constructors->find_by_predicate($type);
|
||||
if ($tl_elem === false) {
|
||||
$id = \danog\PHP\Struct::unpack('<i', fread($bytes_io, 4)) [0];
|
||||
$tl_elem = $this->constructors->find_by_id($id);
|
||||
if ($tl_elem === false) {
|
||||
throw new Exception('Could not extract type: '.$type.' with id '.$id);
|
||||
}
|
||||
}
|
||||
|
||||
$base_boxed_types = ['Vector t', 'Int', 'Long', 'Double', 'String', 'Int128', 'Int256'];
|
||||
if (in_array($tl_elem['type'], $base_boxed_types)) {
|
||||
$x = $this->deserialize($bytes_io, $tl_elem['predicate'], $subtype);
|
||||
} else {
|
||||
$x = ['_' => $tl_elem['predicate']];
|
||||
foreach ($tl_elem['params'] as $arg) {
|
||||
if ($arg['flag']) {
|
||||
switch ($arg['type']) {
|
||||
case 'true':
|
||||
case 'false':
|
||||
|
||||
$x[$arg['name']] = ($x['flags'] & $arg['pow']) == 1;
|
||||
continue 2;
|
||||
break;
|
||||
case 'Bool':
|
||||
$default = false;
|
||||
default:
|
||||
$default = null;
|
||||
if (($x['flags'] & $arg['pow']) == 0) {
|
||||
$x[$arg['name']] = $default;
|
||||
//\danog\MadelineProto\Logger::log('Skipping '.$arg['name'].' of type '.$arg['type'].'/'.$arg['subtype']);
|
||||
continue 2;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
$x[$arg['name']] = $this->deserialize($bytes_io, $arg['type'], $arg['subtype']);
|
||||
}
|
||||
if (isset($x['flags'])) { // I don't think we need this anymore
|
||||
unset($x['flags']);
|
||||
}
|
||||
}
|
||||
break;
|
||||
return $result;
|
||||
}
|
||||
if ($type['type'] != '' && $type['type'][0] == '%') {
|
||||
$checkType = substr($type['type'], 1);
|
||||
$constructorData = $this->constructors->find_by_type($checkType);
|
||||
if ($constructorData === false) {
|
||||
throw new Exception('Constructor not found for type: '. $checkType);
|
||||
}
|
||||
} else {
|
||||
$constructorData = $this->constructors->find_by_predicate($type['type']);
|
||||
if ($constructorData === false) {
|
||||
$id = \danog\PHP\Struct::unpack('<i', fread($bytes_io, 4)) [0];
|
||||
$constructorData = $this->constructors->find_by_id($id);
|
||||
if ($constructorData === false) {
|
||||
throw new Exception('Could not extract type: '.$type['type'].' with id '.$id);
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($constructorData['predicate'] == 'gzip_packed') {
|
||||
return $this->deserialize(gzdecode($this->deserialize($bytes_io, ['type' => 'string'])));
|
||||
}
|
||||
$x = ['_' => $constructorData['predicate']];
|
||||
foreach ($constructorData['params'] as $arg) {
|
||||
if ($arg['flag']) {
|
||||
switch ($arg['type']) {
|
||||
case 'true':
|
||||
case 'false':
|
||||
$x[$arg['name']] = ($x['flags'] & $arg['pow']) == 1;
|
||||
continue 2;
|
||||
break;
|
||||
case 'Bool':
|
||||
$default = false;
|
||||
default:
|
||||
$default = null;
|
||||
if (($x['flags'] & $arg['pow']) == 0) {
|
||||
$x[$arg['name']] = $default;
|
||||
continue 2;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
$x[$arg['name']] = $this->deserialize($bytes_io, $arg);
|
||||
}
|
||||
if (isset($x['flags'])) { // I don't think we need this anymore
|
||||
unset($x['flags']);
|
||||
}
|
||||
|
||||
return $x;
|
||||
}
|
||||
|
||||
|
@ -30,6 +30,18 @@ class TLConstructor extends TLParams
|
||||
$this->key++;
|
||||
}
|
||||
|
||||
public function find_by_type($type)
|
||||
{
|
||||
$key = array_search($type, $this->type);
|
||||
|
||||
return ($key === false) ? false : [
|
||||
'id' => $this->id[$key],
|
||||
'predicate' => $this->predicate[$key],
|
||||
'type' => $this->type[$key],
|
||||
'params' => $this->params[$key],
|
||||
];
|
||||
}
|
||||
|
||||
public function find_by_predicate($predicate)
|
||||
{
|
||||
$key = array_search($predicate, $this->predicate);
|
||||
|
@ -18,7 +18,6 @@ class TLParams
|
||||
{
|
||||
foreach ($this->params[$key] as &$param) {
|
||||
$param['flag'] = false;
|
||||
$param['subtype'] = null;
|
||||
if (preg_match('/^flags\.\d*\?/', $param['type'])) {
|
||||
$param['flag'] = true;
|
||||
$flag = explode('?', explode('.', $param['type'])[1]);
|
||||
@ -34,11 +33,9 @@ class TLParams
|
||||
$param['subtype'] = preg_replace(['/.*</', '/>$/'], '', $param['type']);
|
||||
$param['type'] = 'Vector t';
|
||||
}
|
||||
if (preg_match('/^\%/', $param['subtype'])) {
|
||||
$param['subtype'] = lcfirst(preg_replace('/^\%/', '', $param['subtype']));
|
||||
}
|
||||
$param['subtype'] = (($mtproto && $param['subtype'] == 'message') ? 'MT' : '').$param['subtype'];
|
||||
$param['subtype'] = (($mtproto && $param['subtype'] == 'Message') ? 'MT' : '').$param['subtype'];
|
||||
}
|
||||
$param['type'] = (($mtproto && $param['type'] == 'Message') ? 'MT' : '').$param['type'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
33
testing.php
33
testing.php
@ -13,10 +13,10 @@ If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
require_once 'vendor/autoload.php';
|
||||
|
||||
$MadelineProto = new \danog\MadelineProto\API();
|
||||
|
||||
if (file_exists('number.php')) {
|
||||
if (file_exists('number.php') && !file_exists('session.madeline')) {
|
||||
include_once 'number.php';
|
||||
$MadelineProto = new \danog\MadelineProto\API();
|
||||
|
||||
$checkedPhone = $MadelineProto->auth->checkPhone(// auth.checkPhone becomes auth->checkPhone
|
||||
[
|
||||
@ -33,17 +33,34 @@ if (file_exists('number.php')) {
|
||||
}
|
||||
$authorization = $MadelineProto->complete_phone_login($code);
|
||||
var_dump($authorization);
|
||||
echo 'Serializing MadelineProto to session.madeline...'.PHP_EOL;
|
||||
echo 'Wrote '.file_put_contents('session.madeline', serialize($MadelineProto)).' bytes'.PHP_EOL;
|
||||
}
|
||||
echo 'Deserializing MadelineProto from session.madeline...'.PHP_EOL;
|
||||
$MadelineProto = unserialize(file_get_contents('session.madeline'));
|
||||
|
||||
$message = 'Travis ci tests in progress...';
|
||||
$peers = [];
|
||||
foreach (['pwrtelegramgroup', 'pwrtelegramgroupita'] as $user) {
|
||||
$username = $MadelineProto->contacts->resolveUsername(['username' => $user]);
|
||||
var_dump($username);
|
||||
$peers[$user] = ['_' => 'inputPeerChannel', 'channel_id' => $username['peer']['channel_id'], 'access_hash' => $username['chats'][0]['access_hash']];
|
||||
}
|
||||
|
||||
echo 'Serializing MadelineProto to session.madeline...'.PHP_EOL;
|
||||
echo 'Wrote '.file_put_contents('session.madeline', serialize($MadelineProto)).' bytes'.PHP_EOL;
|
||||
foreach ($peers as $peer) {
|
||||
$sentMessage = $MadelineProto->messages->sendMessage(['peer' => $peer, 'message' => $message, 'random_id' => \danog\PHP\Struct::unpack('<q', \phpseclib\Crypt\Random::string(8))[0]]);
|
||||
var_dump($sentMessage);
|
||||
}
|
||||
|
||||
echo 'Deserializing MadelineProto from session.madeline...'.PHP_EOL;
|
||||
$unserialized = unserialize(file_get_contents('session.madeline'));
|
||||
echo 'Size of MadelineProto instance is '.strlen(serialize($MadelineProto)).' bytes'.PHP_EOL;
|
||||
|
||||
if (file_exists('token.php')) {
|
||||
include_once 'token.php';
|
||||
$authorization = $unserialized->bot_login($token);
|
||||
$MadelineProto = new \danog\MadelineProto\API();
|
||||
$authorization = $MadelineProto->bot_login($token);
|
||||
var_dump($authorization);
|
||||
}
|
||||
echo 'Size of MadelineProto instance is '.strlen(serialize($unserialized)).' bytes'.PHP_EOL;
|
||||
foreach ($peers as $peer) {
|
||||
$sentMessage = $MadelineProto->messages->sendMessage(['peer' => $peer, 'message' => $message, 'random_id' => \danog\PHP\Struct::unpack('<q', \phpseclib\Crypt\Random::string(8))[0]]);
|
||||
var_dump($sentMessage);
|
||||
}
|
||||
|
BIN
token.php.enc
BIN
token.php.enc
Binary file not shown.
Loading…
Reference in New Issue
Block a user