Fixed bugs, updated docs, fixed serialization and deserialization of class, improved TL class

This commit is contained in:
Daniil Gentili 2016-11-25 20:52:56 +00:00
parent 1dca7843a9
commit c6f1642264
9 changed files with 108 additions and 143 deletions

2
.gitignore vendored
View File

@ -66,3 +66,5 @@ vendor
number.php
token.php
*~uploading*
session.mad
*.madeline

124
README.md
View File

@ -17,7 +17,7 @@ This project is in beta state.
This project depends on [PHPStruct](https://github.com/danog/PHPStruct), [phpseclib](https://github.com/phpseclib/phpseclib), https://packagist.org/packages/paragonie/constant_time_encoding and https://packagist.org/packages/paragonie/random_compat
To install them all simply run:
To install dependencies install composer and run:
```
composer update
```
@ -27,13 +27,13 @@ In the cloned repo.
### Instantiation
```
$madeline = new \danog\MadelineProto\API();
$MadelineProto = new \danog\MadelineProto\API();
```
### Settings
The constructor accepts an optional parameter, which is the settings array.
Here you can see its default value and explanations for every setting:
The constructor accepts an optional parameter, which is the settings array. This array contains some other arrays, which are the settings for a specific MadelineProto function.
Here you can see the default values for the settings\ arrays and explanations for every setting:
```
$settings = [
'authorization' => [ // Authorization settings
@ -86,60 +86,53 @@ Slv8kg9qv1m6XHVQY3PnEw+QQtqSIXklHwIDAQAB
2 => [ // The rest will be fetched using help.getConfig
'ip_address' => '2001:067c:04e8:f002:0000:0000:0000:000a',
'port' => 443,
'media_only' => false,
'tcpo_only' => false
]
]
],
],
'connection_settings' => [ // connection settings
'all' => [ // Connection settings will be applied on datacenter ids matching the key of these settings subarrays, if the key is equal to all like in this case that will match all datacenters that haven't a custom settings subarray...
'protocol' => 'tcp_full', // can be tcp_full, tcp_abridged, tcp_intermediate, http (unsupported), https (unsupported), udp (unsupported)
'test_mode' => false, // decides whether to connect to the main telegram servers or to the testing servers (deep telegram)
'ipv6' => $this->ipv6, // decides whether to use ipv6, ipv6 attribute of API attribute of API class contains autodetected boolean
'timeout' => 10 // timeout for sockets
],
],
'app_info' => [ // obtained in https://my.telegram.org
'api_id' => 25628,
'api_hash' => '1fe17cda7d355166cdaa71f04122873c',
'device_model' => php_uname('s'),
'system_version' => php_uname('r'),
'app_version' => 'Unicorn', // 🌚
'lang_code' => 'en',
],
'tl_schema' => [ // TL scheme files
'layer' => 57, // layer version
'src' => [
'mtproto' => __DIR__.'/TL_mtproto_v1.json', // mtproto TL scheme
'telegram' => __DIR__.'/TL_telegram_v57.json', // telegram TL scheme
],
],
'logger' => [ // Logger settings
/*
* logger modes:
* 0 - No logger
* 1 - Log to the default logger destination
* 2 - Log to file defined in second parameter
* 3 - Echo logs
*/
'logger' => 1, // write to
'logger_param' => '/tmp/MadelineProto.log',
'logger' => 3, // overwrite previous setting and echo logs
],
'max_tries' => [
'query' => 5, // How many times should I try to call a method or send an object before throwing an exception
'authorization' => 5, // How many times should I try to generate an authorization key before throwing an exception
'response' => 5,// How many times should I try to get a response of a query before throwing an exception
],
'msg_array_limit' => [ // How big should be the arrays containing the incoming and outgoing messages?
'incoming' => 30,
'outgoing' => 30,
],
];
'media_only' => false,
'tcpo_only' => false
]
]
],
],
'connection_settings' => [ // connection settings
'all' => [ // Connection settings will be applied on datacenter ids matching the key of these settings subarrays, if the key is equal to all like in this case that will match all datacenters that haven't a custom settings subarray...
'protocol' => 'tcp_full', // can be tcp_full, tcp_abridged, tcp_intermediate, http (unsupported), https (unsupported), udp (unsupported)
'test_mode' => false, // decides whether to connect to the main telegram servers or to the testing servers (deep telegram)
'ipv6' => $this->ipv6, // decides whether to use ipv6, ipv6 attribute of API attribute of API class contains autodetected boolean
'timeout' => 10 // timeout for sockets
],
],
'app_info' => [ // obtained in https://my.telegram.org
'api_id' => 25628,
'api_hash' => '1fe17cda7d355166cdaa71f04122873c',
'device_model' => php_uname('s'),
'system_version' => php_uname('r'),
'app_version' => 'Unicorn', // 🌚
'lang_code' => 'en',
],
'tl_schema' => [ // TL scheme files
'layer' => 57, // layer version
'src' => [
'mtproto' => __DIR__.'/TL_mtproto_v1.json', // mtproto TL scheme
'telegram' => __DIR__.'/TL_telegram_v57.json', // telegram TL scheme
],
],
'logger' => [ // Logger settings
'logger' => 1, // 0 - No logger, 1 - Log to the default logger destination, 2 - Log to file defined in logger_param, 3 - Echo logs
'logger_param' => '/tmp/MadelineProto.log',
'logger' => 3, // overwrite previous setting and echo logs
],
'max_tries' => [
'query' => 5, // How many times should I try to call a method or send an object before throwing an exception
'authorization' => 5, // How many times should I try to generate an authorization key before throwing an exception
'response' => 5,// How many times should I try to get a response of a query before throwing an exception
],
'msg_array_limit' => [ // How big should be the arrays containing the incoming and outgoing messages?
'incoming' => 30,
'outgoing' => 30,
],
];
```
You can provide only part of any of the subsettings array, the rest will be automagically set to the default values of the specified subsettings array.
You can provide part of any subsetting array, that way the remaining arrays will be automagically set to default and undefined values of specified subsetting arrays will be set to the default values.
Example:
```
$settings = [
@ -162,17 +155,16 @@ Efzk2DWgkBluml8OREmvfraX3bkHZJTKX4EQSjBbbdJ2ZXIsRrYOXfaA+xayEGB+
Slv8kg9qv1m6XHVQY3PnEw+QQtqSIXklHwIDAQAB
-----END RSA PUBLIC KEY-----',
]
// The remaining subsetting arrays are the set to the default ones
// The remaining subsetting arrays are the set to default
]
```
The rest of the settings array and of the authorization array will be set to the default values specified in the default array.
Note that only subsetting arrays or values of a subsetting array will be set to default.
Note that only settings arrays or values of a settings array will be set to default.
The settings array can be accessed in the instantiated class like this:
```
$MadelineProto = new \danog\MadelineProto\API();
$generated_settings = $MadelineProto->API->settings;
var_dump($MadelineProto->API->settings);
```
### Calling mtproto methods and available wrappers
@ -205,6 +197,10 @@ $authorization = $MadelineProto->bot_login($token); // Note that every time you
var_dump($authorization);
```
### Storing sessions
An istance of MadelineProto can be safely serialized or unserialized.
### Exceptions
MadelineProto can throw three different exceptions:
@ -233,16 +229,16 @@ src/danog/MadelineProto/
TL/
Exception - Handles exceptions in the TL namespace
TL - Handles TL serialization and deserialization
TLConstructor - Represents a TL Constructor
TLMethod - Represents a TL method
API - Wrapper class that istantiates the MTProto class, sets the error handler, provides a wrapper for calling mtproto methods directly as class submethods, and provides some simplified wrappers for logging in to telegram
TLConstructor - Stores TL constructors
TLMethod - Stores TL methods
API - Wrapper class that instantiates the MTProto class, sets the error handler, provides a wrapper for calling mtproto methods directly as class submethods, and provides some simplified wrappers for logging in to telegram
APIFactory - Provides a wrapper for calling namespaced mtproto methods directly as class submethods
Connection - Handles tcp/udp/http connections and wrapping payloads generated by MTProtoTools/MessageHandler into the right message according to the protocol, stores authorization keys, session id and sequence number
DataCenter - Handles mtproto datacenters (is a wrapper for Connection classes)
DebugTools - Various debugging tools
Exception - Handles exceptions and PHP errors
RPCErrorException - Handles RPC errors
MTProto - Extends MTProtoTools, handles initial connection, generation of authorization keys, istantiation of classes, writing of client info
MTProto - Extends MTProtoTools, handles initial connection, generation of authorization keys, instantiation of classes, writing of client info
MTProtoTools - Extends all of the classes in MTProtoTools/
Logger - Static logging class
prime.py and getpq.py - prime module (python) for p and q generation

View File

@ -23,6 +23,7 @@ class API extends APIFactory
set_error_handler(['\danog\MadelineProto\Exception', 'ExceptionErrorHandler']);
$this->API = new MTProto($params);
\danog\MadelineProto\Logger::log('Running APIFactory...');
$this->APIFactory();
\danog\MadelineProto\Logger::log('Ping...');
@ -38,26 +39,28 @@ class API extends APIFactory
public function APIFactory()
{
\danog\MadelineProto\Logger::log('Running APIFactory...');
foreach ($this->API->tl->methods->method_namespace as $namespace => $method) {
$this->{$method} = new APIFactory($method, $this->API);
foreach ($this->API->tl->methods->method_namespace as $namespace) {
$this->{$namespace} = new APIFactory($namespace, $this->API);
}
}
public function logout()
{
$this->API->datacenter->authorized = false;
$this->API->datacenter->authorization = null;
set_error_handler(['\danog\MadelineProto\Exception', 'ExceptionErrorHandler']);
if (!$this->API->method_call('auth.logOut')) {
throw new Exception('An error occurred while logging out!');
}
$this->API->datacenter->authorized = false;
$this->API->datacenter->authorization = null;
\danog\MadelineProto\Logger::log('Logged out successfully!');
restore_error_handler();
return true;
}
public function bot_login($token)
{
set_error_handler(['\danog\MadelineProto\Exception', 'ExceptionErrorHandler']);
if ($this->API->datacenter->authorized) {
\danog\MadelineProto\Logger::log('This instance of MadelineProto is already logged in. Logging out first...');
$this->logout();
@ -73,12 +76,14 @@ class API extends APIFactory
);
$this->API->datacenter->authorized = true;
\danog\MadelineProto\Logger::log('Logged in successfully!');
restore_error_handler();
return $this->API->datacenter->authorization;
}
public function phone_login($number, $sms_type = 5)
{
set_error_handler(['\danog\MadelineProto\Exception', 'ExceptionErrorHandler']);
if ($this->API->datacenter->authorized) {
\danog\MadelineProto\Logger::log('This instance of MadelineProto is already logged in. Logging out first...');
$this->logout();
@ -97,12 +102,14 @@ class API extends APIFactory
$this->API->datacenter->authorization['phone_number'] = $number;
$this->API->datacenter->waiting_code = true;
\danog\MadelineProto\Logger::log('Code sent successfully! Once you receive the code you should use the complete_phone_login function.');
restore_error_handler();
return $this->API->datacenter->authorization;
}
public function complete_phone_login($code)
{
set_error_handler(['\danog\MadelineProto\Exception', 'ExceptionErrorHandler']);
if (!$this->API->datacenter->waiting_code) {
throw new Exception("I'm not waiting for the code! Please call the phone_login method first");
}
@ -118,10 +125,15 @@ class API extends APIFactory
$this->API->datacenter->waiting_code = false;
$this->API->datacenter->authorized = true;
\danog\MadelineProto\Logger::log('Logged in successfully!');
restore_error_handler();
return $this->API->datacenter->authorization;
}
public function __sleep()
{
return ["API"];
}
public function __wakeup()
{
$this->APIFactory();

View File

@ -114,7 +114,7 @@ class Connection extends Tools
public function __wakeup()
{
$this->close_and_reopen();
$this->__construct($this->ip, $this->port, $this->protocol, $this->timeout);
}
/**

View File

@ -159,7 +159,8 @@ Slv8kg9qv1m6XHVQY3PnEw+QQtqSIXklHwIDAQAB
$this->setup_logger();
// Connect to servers
$this->mk_datacenter();
\danog\MadelineProto\Logger::log('Istantiating DataCenter...');
$this->datacenter = new DataCenter($this->settings['connection'], $this->settings['connection_settings']);
// Load rsa key
\danog\MadelineProto\Logger::log('Loading RSA key...');
@ -176,14 +177,7 @@ Slv8kg9qv1m6XHVQY3PnEw+QQtqSIXklHwIDAQAB
public function __wakeup()
{
$this->setup_logger();
$this->mk_datacenter();
}
public function mk_datacenter()
{
// Connect to servers
\danog\MadelineProto\Logger::log('Istantiating DataCenter...');
$this->datacenter = new DataCenter($this->settings['connection'], $this->settings['connection_settings']);
$this->datacenter->__construct($this->settings['connection'], $this->settings['connection_settings']);
}
public function setup_logger()
@ -278,5 +272,6 @@ Slv8kg9qv1m6XHVQY3PnEw+QQtqSIXklHwIDAQAB
$test .= (isset($this->settings['connection'][$test][$ipv6][$id]) && $this->settings['connection'][$test][$ipv6][$id]['ip_address'] != $dc['ip_address']) ? '_bk' : '';
$this->settings['connection'][$test][$ipv6][$id] = $dc;
}
unset($this->config['dc_options']);
}
}

View File

@ -80,21 +80,20 @@ class TL extends \danog\MadelineProto\Tools
$flags = 0;
foreach ($tl['params'] as $cur_flag) {
if ($cur_flag['flag']) {
$flag_pow = pow(2, $cur_flag['pow']);
switch ($cur_flag['type']) {
case 'true':
case 'false':
$flags = (isset($arguments[$cur_flag['name']]) && $arguments[$cur_flag['name']]) ? ($flags | $flag_pow) : ($flags & ~$flag_pow);
$flags = (isset($arguments[$cur_flag['name']]) && $arguments[$cur_flag['name']]) ? ($flags | $cur_flag['pow']) : ($flags & ~$cur_flag['pow']);
unset($arguments[$cur_flag['name']]);
break;
case 'Bool':
$arguments[$cur_flag['name']] = (isset($arguments[$cur_flag['name']]) && $arguments[$cur_flag['name']]) && (($flags & $flag_pow) != 0);
if (($flags & $flag_pow) == 0) {
$arguments[$cur_flag['name']] = (isset($arguments[$cur_flag['name']]) && $arguments[$cur_flag['name']]) && (($flags & $cur_flag['pow']) != 0);
if (($flags & $cur_flag['pow']) == 0) {
unset($arguments[$cur_flag['name']]);
}
break;
default:
$flags = (isset($arguments[$cur_flag['name']]) && $arguments[$cur_flag['name']] !== null) ? ($flags | $flag_pow) : ($flags & ~$flag_pow);
$flags = (isset($arguments[$cur_flag['name']]) && $arguments[$cur_flag['name']] !== null) ? ($flags | $cur_flag['pow']) : ($flags & ~$cur_flag['pow']);
break;
}
}
@ -102,7 +101,7 @@ class TL extends \danog\MadelineProto\Tools
$arguments['flags'] = $flags;
foreach ($tl['params'] as $current_argument) {
if (!isset($arguments[$current_argument['name']])) {
if ($current_argument['flag'] && (in_array($current_argument['type'], ['true', 'false']) || ($flags & pow(2, $current_argument['pow'])) == 0)) {
if ($current_argument['flag'] && (in_array($current_argument['type'], ['true', 'false']) || ($flags & $current_argument['pow']) == 0)) {
//\danog\MadelineProto\Logger::log('Skipping '.$current_argument['name'].' of type '.$current_argument['type'].'/'.$current_argument['subtype']);
continue;
}
@ -182,12 +181,7 @@ class TL extends \danog\MadelineProto\Tools
return $concat;
default:
$tl_elem = $this->constructors->find_by_predicate($type);
if ($tl_elem === false) {
throw new Exception('Could not serialize type: '.$type);
}
return \danog\PHP\Struct::pack('<i', $tl_elem['id']);
return $this->serialize_generic($type, $value);
break;
}
}
@ -286,19 +280,18 @@ class TL extends \danog\MadelineProto\Tools
$x = ['_' => $tl_elem['predicate']];
foreach ($tl_elem['params'] as $arg) {
if ($arg['flag']) {
$flag_pow = pow(2, $arg['pow']);
switch ($arg['type']) {
case 'true':
case 'false':
$x[$arg['name']] = ($x['flags'] & $flag_pow) == 1;
$x[$arg['name']] = ($x['flags'] & $arg['pow']) == 1;
continue 2;
break;
case 'Bool':
$default = false;
default:
$default = null;
if (($x['flags'] & $flag_pow) == 0) {
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;

View File

@ -12,7 +12,7 @@ If not, see <http://www.gnu.org/licenses/>.
namespace danog\MadelineProto\TL;
class TLConstructor
class TLConstructor extends TLParams
{
public $id = [];
public $predicate = [];
@ -20,35 +20,14 @@ class TLConstructor
public $params = [];
public $key = 0;
public function add($json_dict, $mtproto)
{
$this->id[$this->key] = (int) $json_dict['id'];
$this->predicate[$this->key] = (string) ((($mtproto && $json_dict['predicate'] == 'message') ? 'MT' : '').$json_dict['predicate']);
$this->type[$this->key] = $json_dict['type'];
$this->params[$this->key] = $json_dict['params'];
foreach ($this->params[$this->key] as &$param) {
$param['flag'] = false;
$param['subtype'] = null;
if (preg_match('/^flags\.\d*\?/', $param['type'])) {
$param['flag'] = true;
$param['pow'] = preg_replace(['/^flags\./', '/\?.*/'], '', $param['type']);
$param['type'] = preg_replace('/^flags\.\d*\?/', '', $param['type']);
}
if (preg_match('/vector<.*>/i', $param['type'])) {
if (preg_match('/vector/', $param['type'])) {
$param['subtype'] = preg_replace(['/.*</', '/>$/'], '', $param['type']);
$param['type'] = 'vector';
}
if (preg_match('/Vector/', $param['type'])) {
$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'];
}
}
$this->parse_params($this->key, $mtproto);
$this->key++;
}

View File

@ -12,7 +12,7 @@ If not, see <http://www.gnu.org/licenses/>.
namespace danog\MadelineProto\TL;
class TLMethod
class TLMethod extends TLParams
{
public $id = [];
public $method = [];
@ -32,28 +32,7 @@ class TLMethod
$this->method_namespace[$namespace[0]] = $namespace[0];
}
foreach ($this->params[$this->key] as &$param) {
$param['flag'] = false;
$param['subtype'] = null;
if (preg_match('/^flags\.\d*\?/', $param['type'])) {
$param['flag'] = true;
$param['pow'] = preg_replace(['/^flags\./', '/\?.*/'], '', $param['type']);
$param['type'] = preg_replace('/^flags\.\d*\?/', '', $param['type']);
}
if (preg_match('/vector<.*>/i', $param['type'])) {
if (preg_match('/vector/', $param['type'])) {
$param['subtype'] = preg_replace(['/.*</', '/>$/'], '', $param['type']);
$param['type'] = 'vector';
}
if (preg_match('/Vector/', $param['type'])) {
$param['subtype'] = preg_replace(['/.*</', '/>$/'], '', $param['type']);
$param['type'] = 'Vector t';
}
if (preg_match('/^\%/', $param['subtype'])) {
$param['subtype'] = lcfirst(preg_replace('/^\%/', '', $param['subtype']));
}
}
}
$this->parse_params($this->key);
$this->key++;
}

View File

@ -34,8 +34,17 @@ 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;
$unserialized = unserialize(file_get_contents('session.madeline'));
if (file_exists('token.php')) {
include_once 'token.php';
$MadelineProto->bot_login($token);
$authorization = $unserialized->bot_login($token);
var_dump($authorization);
}
echo 'Size of MadelineProto instance is '.strlen(var_export($MadelineProto, true)).' bytes'.PHP_EOL;
echo 'Size of MadelineProto instance is '.strlen(serialize($unserialized)).' bytes'.PHP_EOL;