Added documentation, simplified code, organized exceptions, added some more examples in testing.php, decided to unset flags in deserialized responses, moved message id arrays to Connection classes, added wrappers for logging in to telegram as a bot or as a user and for logging out, fixed deserializing of gzip packed objects, added more logging, fixed bugs, added methods to get and parse configuration, saved some fairies, fixed exporting/importing of authorization, added some wakeup methods to prevent problems during serialization, added support for ipv6 and automagical detection too. I think we can safely say this is now a beta.
This commit is contained in:
parent
c168890384
commit
7be648fb20
1
.gitignore
vendored
1
.gitignore
vendored
@ -65,3 +65,4 @@ vendor
|
||||
*bak
|
||||
number.php
|
||||
token.php
|
||||
*~uploading*
|
||||
|
214
README.md
214
README.md
@ -2,15 +2,211 @@
|
||||
[![StyleCI](https://styleci.io/repos/61838413/shield)](https://styleci.io/repos/61838413)
|
||||
[![Build Status](https://travis-ci.org/danog/MadelineProto.svg?branch=master)](https://travis-ci.org/danog/MadelineProto)
|
||||
|
||||
Licensed under AGPLv3.
|
||||
Created by [Daniil Gentili](https://daniil.it), licensed under AGPLv3.
|
||||
|
||||
PHP implementation of MTProto, based on [telepy](https://github.com/griganton/telepy_old).
|
||||
|
||||
This project can run on PHP 7, PHP 5.6 and HHVM.
|
||||
|
||||
This is a WIP.
|
||||
This project is in beta state.
|
||||
|
||||
Structure of this project:
|
||||
|
||||
## Usage
|
||||
|
||||
### Instantiation
|
||||
|
||||
```
|
||||
$madeline = 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:
|
||||
```
|
||||
$settings = [
|
||||
'authorization' => [ // Authorization settings
|
||||
'default_temp_auth_key_expires_in' => 31557600, // validity of temporary keys and the binding of the temporary and permanent keys
|
||||
'rsa_key' => '-----BEGIN RSA PUBLIC KEY-----
|
||||
MIIBCgKCAQEAwVACPi9w23mF3tBkdZz+zwrzKOaaQdr01vAbU4E1pvkfj4sqDsm6
|
||||
lyDONS789sVoD/xCS9Y0hkkC3gtL1tSfTlgCMOOul9lcixlEKzwKENj1Yz/s7daS
|
||||
an9tqw3bfUV/nqgbhGX81v/+7RFAEd+RwFnK7a+XYl9sluzHRyVVaTTveB2GazTw
|
||||
Efzk2DWgkBluml8OREmvfraX3bkHZJTKX4EQSjBbbdJ2ZXIsRrYOXfaA+xayEGB+
|
||||
8hdlLmAjbCVfaigxX0CDqWeR1yFL9kwd9P0NsZRPsmoqVwMbMu7mStFai6aIhc3n
|
||||
Slv8kg9qv1m6XHVQY3PnEw+QQtqSIXklHwIDAQAB
|
||||
-----END RSA PUBLIC KEY-----', // RSA public key
|
||||
],
|
||||
'connection' => [ // List of datacenters/subdomains where to connect
|
||||
'ssl_subdomains' => [ // Subdomains of web.telegram.org for https protocol
|
||||
1 => 'pluto',
|
||||
2 => 'venus',
|
||||
3 => 'aurora',
|
||||
4 => 'vesta',
|
||||
5 => 'flora', // musa oh wait no :(
|
||||
],
|
||||
'test' => [ // Test datacenters
|
||||
'ipv4' => [ // ipv4 addresses
|
||||
2 => [ // The rest will be fetched using help.getConfig
|
||||
'ip_address' => '149.154.167.40',
|
||||
'port' => 443,
|
||||
'media_only' => false,
|
||||
'tcpo_only' => false
|
||||
]
|
||||
],
|
||||
'ipv6' => [ // ipv6 addresses
|
||||
2 => [ // The rest will be fetched using help.getConfig
|
||||
'ip_address' => '2001:067c:04e8:f002:0000:0000:0000:000e',
|
||||
'port' => 443,
|
||||
'media_only' => false,
|
||||
'tcpo_only' => false
|
||||
]
|
||||
]
|
||||
],
|
||||
'main' => [ // Main datacenters
|
||||
'ipv4' => [ // ipv4 addresses
|
||||
2 => [ // The rest will be fetched using help.getConfig
|
||||
'ip_address' => '149.154.167.51',
|
||||
'port' => 443,
|
||||
'media_only' => false,
|
||||
'tcpo_only' => false
|
||||
]
|
||||
],
|
||||
'ipv6' => [ // ipv6 addresses
|
||||
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,
|
||||
],
|
||||
];
|
||||
```
|
||||
|
||||
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.
|
||||
Example:
|
||||
```
|
||||
$settings = [
|
||||
'authorization' => [ // Authorization settings
|
||||
'default_temp_auth_key_expires_in' => 86400, // a day
|
||||
]
|
||||
]
|
||||
```
|
||||
Becomes:
|
||||
```
|
||||
$settings = [
|
||||
'authorization' => [ // Authorization settings
|
||||
'default_temp_auth_key_expires_in' => 86400,
|
||||
'rsa_key' => '-----BEGIN RSA PUBLIC KEY-----
|
||||
MIIBCgKCAQEAwVACPi9w23mF3tBkdZz+zwrzKOaaQdr01vAbU4E1pvkfj4sqDsm6
|
||||
lyDONS789sVoD/xCS9Y0hkkC3gtL1tSfTlgCMOOul9lcixlEKzwKENj1Yz/s7daS
|
||||
an9tqw3bfUV/nqgbhGX81v/+7RFAEd+RwFnK7a+XYl9sluzHRyVVaTTveB2GazTw
|
||||
Efzk2DWgkBluml8OREmvfraX3bkHZJTKX4EQSjBbbdJ2ZXIsRrYOXfaA+xayEGB+
|
||||
8hdlLmAjbCVfaigxX0CDqWeR1yFL9kwd9P0NsZRPsmoqVwMbMu7mStFai6aIhc3n
|
||||
Slv8kg9qv1m6XHVQY3PnEw+QQtqSIXklHwIDAQAB
|
||||
-----END RSA PUBLIC KEY-----',
|
||||
]
|
||||
// The remaining subsetting arrays are the set to the default ones
|
||||
]
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
The settings array can be accessed in the instantiated class like this:
|
||||
```
|
||||
$MadelineProto = new \danog\MadelineProto\API();
|
||||
$generated_settings = $MadelineProto->API->settings;
|
||||
```
|
||||
|
||||
### Calling mtproto methods and available wrappers
|
||||
|
||||
A list of mtproto methods can be found [here](https://tjhorner.com/tl-schema/#functions).
|
||||
To call an MTProto method simply call it as if it is a method of the API class, substitute namespace sepators (.) with -> if needed:
|
||||
```
|
||||
$MadelineProto = new \danog\MadelineProto\API();
|
||||
$checkedPhone = $MadelineProto->auth->checkPhone( // auth.checkPhone becomes auth->checkPhone
|
||||
[
|
||||
'phone_number' => '3993838383', // Random invalid number, note that there should be no +
|
||||
]
|
||||
);
|
||||
$ping = $MadelineProto->ping([3]); // parameter names can be omitted as long as the order specified by the TL scheme is respected
|
||||
```
|
||||
|
||||
The API class also provides some wrapper methods for logging in as a bot or as a normal user:
|
||||
```
|
||||
$sentCode = $MadelineProto->phone_login($number); // Send code
|
||||
var_dump($sentCode);
|
||||
echo 'Enter the code you received: ';
|
||||
$code = '';
|
||||
for ($x = 0; $x < $sentCode['type']['length']; $x++) {
|
||||
$code .= fgetc(STDIN);
|
||||
}
|
||||
$authorization = $MadelineProto->complete_phone_login($code); // Complete authorization
|
||||
var_dump($authorization);
|
||||
|
||||
$authorization = $MadelineProto->bot_login($token); // Note that every time you login as a bot or as a user MadelineProto will logout first, so now MadelineProto is logged in as the bot with the token $token, not with the number $number
|
||||
var_dump($authorization);
|
||||
```
|
||||
|
||||
### Exceptions
|
||||
|
||||
MadelineProto can throw three different exceptions:
|
||||
* \danog\MadelineProto\Exception - Default exception, thrown when a php error occures and in a lot of other cases
|
||||
* \danog\MadelineProto\RPCErrorException - Thrown when an RPC error occurres (an error received via the mtproto API)
|
||||
* \danog\MadelineProto\TL/Exception - Thrown on TL serialization/deserialization errors
|
||||
|
||||
|
||||
## Contributing
|
||||
|
||||
[Here](https://github.com/danog/MadelineProto/projects/1) you can find this project's roadmap.
|
||||
|
||||
You can use this scheme of the structure of this project to help yourself:
|
||||
```
|
||||
src/danog/MadelineProto/
|
||||
MTProtoTools/
|
||||
@ -18,7 +214,6 @@ src/danog/MadelineProto/
|
||||
AuthKeyHandler - Handles generation of the temporary and permanent authorization keys
|
||||
CallHandler - Handles synchronous calls to mtproto methods or objects, also basic response management (waits until the socket receives a response)
|
||||
Crypt - Handles ige and aes encryption
|
||||
Exception - Handles exceptions in the MTProtoTools namespace
|
||||
MessageHandler - Handles sending and receiving of mtproto messages (packs TL serialized data with message id, auth key id and encrypts it with Crypt if needed, adds them to the arrays of incoming and outgoing messages)
|
||||
MsgIdHandler - Handles message ids (checks if they are valid, adds them to the arrays of incoming and outgoing messages)
|
||||
ResponseHandler - Handles the content of responses received, service messages, rpc results, errors, and stores them into the response section of the outgoing messages array)
|
||||
@ -29,22 +224,21 @@ src/danog/MadelineProto/
|
||||
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, and provides a wrapper for calling mtproto methods directly as class submethods
|
||||
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
|
||||
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 in the main namespace
|
||||
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
|
||||
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
|
||||
PrimeModule.php - prime module (php) for p and q generation by wrapping the python module, using wolfram alpha or a built in PHP engine
|
||||
RSA - Handles RSA public keys and signatures
|
||||
Tools - Various tools (positive modulus, string2bin, python-like range)
|
||||
```
|
||||
|
||||
|
||||
This project adheres to the [Hacktoberfest](https://hacktoberfest.digitalocean.com/) event by DigitalOcean.
|
||||
Browse the issues of this project and help close at least four of them with a pull request to get a free t-shirt!
|
||||
Check out the [Contribution guide first though](https://github.com/danog/MadelineProto/blob/master/CONTRIBUTING.md).
|
||||
Check out the [Contribution guide](https://github.com/danog/MadelineProto/blob/master/CONTRIBUTING.md) before contributing.
|
||||
|
||||
|
@ -22,11 +22,9 @@ class API extends APIFactory
|
||||
{
|
||||
set_error_handler(['\danog\MadelineProto\Exception', 'ExceptionErrorHandler']);
|
||||
$this->API = new MTProto($params);
|
||||
\danog\MadelineProto\Logger::log('Running APIFactory...');
|
||||
foreach ($this->API->tl->methods->method_namespace as $namespace => $method) {
|
||||
$this->{$method} = new APIFactory($method, $this->API);
|
||||
}
|
||||
|
||||
|
||||
$this->APIFactory();
|
||||
|
||||
\danog\MadelineProto\Logger::log('Ping...');
|
||||
$pong = $this->ping([3]);
|
||||
\danog\MadelineProto\Logger::log('Pong: '.$pong['ping_id']);
|
||||
@ -37,7 +35,83 @@ class API extends APIFactory
|
||||
\danog\MadelineProto\Logger::log('MadelineProto is ready!');
|
||||
restore_error_handler();
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
public function logout() {
|
||||
$this->API->datacenter->authorized = false;
|
||||
$this->API->datacenter->authorization = null;
|
||||
if (!$this->API->method_call('auth.logOut')) {
|
||||
throw new Exception('An error occurred while logging out!');
|
||||
}
|
||||
\danog\MadelineProto\Logger::log('Logged out successfully!');
|
||||
return true;
|
||||
}
|
||||
public function bot_login($token) {
|
||||
if ($this->API->datacenter->authorized) {
|
||||
\danog\MadelineProto\Logger::log('This instance of MadelineProto is already logged in. Logging out first...');
|
||||
$this->logout();
|
||||
}
|
||||
\danog\MadelineProto\Logger::log('Logging in as a bot...');
|
||||
$this->API->datacenter->authorization = $this->API->method_call(
|
||||
'auth.importBotAuthorization',
|
||||
[
|
||||
'bot_auth_token' => $token,
|
||||
'api_id' => $this->API->settings['app_info']['api_id'],
|
||||
'api_hash' => $this->API->settings['app_info']['api_hash'],
|
||||
]
|
||||
);
|
||||
$this->API->datacenter->authorized = true;
|
||||
\danog\MadelineProto\Logger::log('Logged in successfully!');
|
||||
return $this->API->datacenter->authorization;
|
||||
}
|
||||
public function phone_login($number, $sms_type = 5) {
|
||||
if ($this->API->datacenter->authorized) {
|
||||
\danog\MadelineProto\Logger::log('This instance of MadelineProto is already logged in. Logging out first...');
|
||||
$this->logout();
|
||||
}
|
||||
\danog\MadelineProto\Logger::log('Sending code...');
|
||||
$this->API->datacenter->authorization = $this->API->method_call(
|
||||
'auth.sendCode',
|
||||
[
|
||||
'phone_number' => $number,
|
||||
'sms_type' => $sms_type,
|
||||
'api_id' => $this->API->settings['app_info']['api_id'],
|
||||
'api_hash' => $this->API->settings['app_info']['api_hash'],
|
||||
'lang_code' => $this->API->settings['app_info']['lang_code'],
|
||||
]
|
||||
);
|
||||
$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.');
|
||||
return $this->API->datacenter->authorization;
|
||||
}
|
||||
public function complete_phone_login($code) {
|
||||
if (!$this->API->datacenter->waiting_code) {
|
||||
throw new Exception("I'm not waiting for the code! Please call the phone_login method first");
|
||||
}
|
||||
\danog\MadelineProto\Logger::log('Logging in as a normal user...');
|
||||
$this->API->datacenter->authorization = $this->API->method_call(
|
||||
'auth.signIn',
|
||||
[
|
||||
'phone_number' => $this->API->datacenter->authorization['phone_number'],
|
||||
'phone_code_hash' => $this->API->datacenter->authorization['phone_code_hash'],
|
||||
'phone_code' => $code,
|
||||
]
|
||||
);
|
||||
$this->API->datacenter->waiting_code = false;
|
||||
$this->API->datacenter->authorized = true;
|
||||
\danog\MadelineProto\Logger::log('Logged in successfully!');
|
||||
return $this->API->datacenter->authorization;
|
||||
}
|
||||
public function __wakeup() {
|
||||
$this->APIFactory();
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
restore_error_handler();
|
||||
|
@ -26,7 +26,8 @@ class APIFactory
|
||||
public function __call($name, $arguments)
|
||||
{
|
||||
set_error_handler(['\danog\MadelineProto\Exception', 'ExceptionErrorHandler']);
|
||||
return $this->API->method_call($this->namespace.$name, $arguments[0]);
|
||||
$this->API->get_config();
|
||||
return $this->API->method_call($this->namespace.$name, is_array($arguments[0]) ? $arguments[0] : []);
|
||||
restore_error_handler();
|
||||
}
|
||||
}
|
||||
|
@ -29,7 +29,14 @@ class Connection extends Tools
|
||||
public $auth_key;
|
||||
public $session_id;
|
||||
public $seq_no = 0;
|
||||
|
||||
public $authorized = false;
|
||||
public $authorization = null;
|
||||
public $waiting_code = false;
|
||||
|
||||
|
||||
public $incoming_messages = [];
|
||||
public $outgoing_messages = [];
|
||||
|
||||
public function __construct($ip, $port, $protocol, $timeout)
|
||||
{
|
||||
// Can use:
|
||||
@ -104,7 +111,9 @@ class Connection extends Tools
|
||||
$this->__destruct();
|
||||
$this->__construct($this->ip, $this->port, $this->protocol, $this->timeout);
|
||||
}
|
||||
|
||||
public function __wakeup() {
|
||||
$this->close_and_reopen();
|
||||
}
|
||||
/**
|
||||
* Function to get hex crc32.
|
||||
*
|
||||
|
@ -17,36 +17,22 @@ namespace danog\MadelineProto;
|
||||
*/
|
||||
class DataCenter extends Tools
|
||||
{
|
||||
public $referenced_variables = ['time_delta', 'temp_auth_key', 'auth_key', 'session_id', 'seq_no'];
|
||||
|
||||
public $sockets;
|
||||
public $curdc = 0;
|
||||
public $dclist = [];
|
||||
public $settings = [];
|
||||
|
||||
public function __construct($dclist, $settings)
|
||||
public function __construct(&$dclist, &$settings)
|
||||
{
|
||||
$this->dclist = $dclist;
|
||||
$this->settings = $settings;
|
||||
if (isset($this->settings['all'])) {
|
||||
foreach ($this->range(1, 6) as $n) {
|
||||
$this->settings[$n] = $this->settings['all'];
|
||||
}
|
||||
unset($this->settings['all']);
|
||||
}
|
||||
foreach ($this->range(1, 6) as $n) {
|
||||
if (!isset($this->settings[$n])) {
|
||||
$this->settings[$n] = [
|
||||
'protocol' => 'tcp_full',
|
||||
'port' => '443',
|
||||
'test_mode' => true,
|
||||
'timeout' => 10,
|
||||
];
|
||||
}
|
||||
}
|
||||
$this->dclist = &$dclist;
|
||||
$this->settings = &$settings;
|
||||
}
|
||||
|
||||
public function dc_disconnect($dc_number)
|
||||
{
|
||||
if ($this->curdc == $dc_number) {
|
||||
$this->unset_curdc();
|
||||
$this->curdc = 0;
|
||||
}
|
||||
if (isset($this->sockets[$dc_number])) {
|
||||
\danog\MadelineProto\Logger::log('Disconnecting from DC '.$dc_number.'...');
|
||||
@ -56,53 +42,40 @@ class DataCenter extends Tools
|
||||
|
||||
public function dc_connect($dc_number, $settings = [])
|
||||
{
|
||||
$this->curdc = $dc_number;
|
||||
if (isset($this->sockets[$dc_number])) {
|
||||
$this->set_curdc($dc_number);
|
||||
return false;
|
||||
}
|
||||
|
||||
\danog\MadelineProto\Logger::log('Connecting to DC '.$dc_number.'...');
|
||||
|
||||
if ($settings == []) {
|
||||
$settings = $this->settings[$dc_number];
|
||||
}
|
||||
$address = $settings['test_mode'] ? $this->dclist['test'][$dc_number] : $this->dclist['main'][$dc_number];
|
||||
$test = $settings['test_mode'] ? 'test' : 'main';
|
||||
$ipv6 = $settings['ipv6'] ? 'ipv6' : 'ipv4';
|
||||
$address = $this->dclist[$test][$ipv6][$dc_number]['ip_address'];
|
||||
$port = $this->dclist[$test][$ipv6][$dc_number]['port'];
|
||||
if ($settings['protocol'] == 'https') {
|
||||
$subdomain = $this->dclist['ssl_subdomains'][$dc_number].($settings['upload'] ? '-1' : '');
|
||||
$path = $settings['test_mode'] ? 'apiw_test1' : 'apiw1';
|
||||
$address = 'https://'.$subdomain.'.web.telegram.org/'.$path;
|
||||
}
|
||||
$this->sockets[$dc_number] = new Connection($address, $settings['port'], $settings['protocol'], $settings['timeout']);
|
||||
$this->set_curdc($dc_number);
|
||||
\danog\MadelineProto\Logger::log('Connecting to DC '.$dc_number.' ('.$test.' server, '.$ipv6.')...');
|
||||
|
||||
$this->sockets[$dc_number] = new Connection($address, $port, $settings['protocol'], $settings['timeout']);
|
||||
return true;
|
||||
}
|
||||
|
||||
public function set_curdc($dc_number)
|
||||
|
||||
public function &__get($name)
|
||||
{
|
||||
$this->curdc = $dc_number;
|
||||
foreach ($this->referenced_variables as $key) {
|
||||
$this->{$key} = &$this->sockets[$dc_number]->{$key};
|
||||
}
|
||||
}
|
||||
|
||||
public function unset_curdc()
|
||||
return $this->sockets[$this->curdc]->{$name};
|
||||
}
|
||||
public function __set($name, $value)
|
||||
{
|
||||
$this->curdc = 0;
|
||||
foreach ($this->referenced_variables as $key) {
|
||||
unset($this->{$key});
|
||||
}
|
||||
$this->sockets[$this->curdc]->{$name} =& $value;
|
||||
}
|
||||
|
||||
public function __call($name, $arguments)
|
||||
{
|
||||
return $this->sockets[$this->curdc]->{$name}(...$arguments);
|
||||
}
|
||||
|
||||
public function __destroy()
|
||||
{
|
||||
$this->unset_curdc();
|
||||
foreach ($this->sockets as $n => $socket) {
|
||||
unset($this->sockets[$n]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -29,13 +29,13 @@ class Logger
|
||||
* 2 - Log to file defined in second parameter
|
||||
* 3 - Echo logs
|
||||
*/
|
||||
public static function constructor($mode, $optional = null)
|
||||
public static function constructor(&$mode, &$optional = null)
|
||||
{
|
||||
if ($mode == null) {
|
||||
throw new Exception('No mode was specified!');
|
||||
}
|
||||
self::$mode = (string) $mode;
|
||||
self::$optional = $optional;
|
||||
self::$mode =& $mode;
|
||||
self::$optional =& $optional;
|
||||
self::$constructed = true;
|
||||
}
|
||||
|
||||
@ -50,13 +50,13 @@ class Logger
|
||||
}
|
||||
$param = str_pad(basename(debug_backtrace()[0]['file'], '.php').': ', 16).((self::$mode == 3) ? "\t" : '').$param;
|
||||
switch (self::$mode) {
|
||||
case '1':
|
||||
case 1:
|
||||
error_log($param);
|
||||
break;
|
||||
case '2':
|
||||
case 2:
|
||||
error_log($param, 3, self::$optional);
|
||||
break;
|
||||
case '3':
|
||||
case 3:
|
||||
echo $param.PHP_EOL;
|
||||
break;
|
||||
default:
|
||||
|
@ -18,13 +18,23 @@ namespace danog\MadelineProto;
|
||||
class MTProto extends MTProtoTools
|
||||
{
|
||||
public $settings = [];
|
||||
|
||||
public $authorized = false;
|
||||
public $waiting_code = false;
|
||||
public $config = ['expires' => -1];
|
||||
public $ipv6 = false;
|
||||
|
||||
public function __construct($settings = [])
|
||||
{
|
||||
$google = '';
|
||||
try {
|
||||
$google = file_get_contents('https://ipv6.google.com');
|
||||
} catch (Exception $e) { ; };
|
||||
$this->ipv6 = strlen($google) > 0;
|
||||
|
||||
// Set default settings
|
||||
$default_settings = [
|
||||
'authorization' => [
|
||||
'default_temp_auth_key_expires_in' => 31557600,
|
||||
'authorization' => [ // Authorization settings
|
||||
'default_temp_auth_key_expires_in' => 31557600, // validity of temporary keys and the binding of the temporary and permanent keys
|
||||
'rsa_key' => '-----BEGIN RSA PUBLIC KEY-----
|
||||
MIIBCgKCAQEAwVACPi9w23mF3tBkdZz+zwrzKOaaQdr01vAbU4E1pvkfj4sqDsm6
|
||||
lyDONS789sVoD/xCS9Y0hkkC3gtL1tSfTlgCMOOul9lcixlEKzwKENj1Yz/s7daS
|
||||
@ -32,64 +42,94 @@ an9tqw3bfUV/nqgbhGX81v/+7RFAEd+RwFnK7a+XYl9sluzHRyVVaTTveB2GazTw
|
||||
Efzk2DWgkBluml8OREmvfraX3bkHZJTKX4EQSjBbbdJ2ZXIsRrYOXfaA+xayEGB+
|
||||
8hdlLmAjbCVfaigxX0CDqWeR1yFL9kwd9P0NsZRPsmoqVwMbMu7mStFai6aIhc3n
|
||||
Slv8kg9qv1m6XHVQY3PnEw+QQtqSIXklHwIDAQAB
|
||||
-----END RSA PUBLIC KEY-----',
|
||||
-----END RSA PUBLIC KEY-----', // RSA public key
|
||||
],
|
||||
'connection' => [
|
||||
'ssl_subdomains' => [
|
||||
'connection' => [ // List of datacenters/subdomains where to connect
|
||||
'ssl_subdomains' => [ // Subdomains of web.telegram.org for https protocol
|
||||
1 => 'pluto',
|
||||
2 => 'venus',
|
||||
3 => 'aurora',
|
||||
4 => 'vesta',
|
||||
5 => 'flora',
|
||||
5 => 'flora', // musa oh wait no :(
|
||||
],
|
||||
'test' => [
|
||||
1 => '149.154.175.10',
|
||||
2 => '149.154.167.40',
|
||||
3 => '149.154.175.117',
|
||||
'test' => [ // Test datacenters
|
||||
'ipv4' => [ // ipv4 addresses
|
||||
2 => [ // The rest will be fetched using help.getConfig
|
||||
'ip_address' => '149.154.167.40',
|
||||
'port' => 443,
|
||||
'media_only' => false,
|
||||
'tcpo_only' => false
|
||||
]
|
||||
],
|
||||
'ipv6' => [ // ipv6 addresses
|
||||
2 => [ // The rest will be fetched using help.getConfig
|
||||
'ip_address' => '2001:067c:04e8:f002:0000:0000:0000:000e',
|
||||
'port' => 443,
|
||||
'media_only' => false,
|
||||
'tcpo_only' => false
|
||||
]
|
||||
]
|
||||
],
|
||||
'main' => [
|
||||
1 => '149.154.175.50',
|
||||
2 => '149.154.167.51',
|
||||
3 => '149.154.175.100',
|
||||
4 => '149.154.167.91',
|
||||
5 => '149.154.171.5',
|
||||
'main' => [ // Main datacenters
|
||||
'ipv4' => [ // ipv4 addresses
|
||||
2 => [ // The rest will be fetched using help.getConfig
|
||||
'ip_address' => '149.154.167.51',
|
||||
'port' => 443,
|
||||
'media_only' => false,
|
||||
'tcpo_only' => false
|
||||
]
|
||||
],
|
||||
'ipv6' => [ // ipv6 addresses
|
||||
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' => [
|
||||
'all' => [
|
||||
'protocol' => 'tcp_full',
|
||||
'test_mode' => false,
|
||||
'port' => '443',
|
||||
'timeout' => 10
|
||||
'connection_settings' => [ // connection settings
|
||||
'all' => [ // These settings will be applied on every datacenter that hasn'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
|
||||
],
|
||||
'default_dc' => 2,
|
||||
],
|
||||
'app_info' => [
|
||||
'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',
|
||||
'app_version' => 'Unicorn', // 🌚
|
||||
'lang_code' => 'en',
|
||||
],
|
||||
'tl_schema' => [
|
||||
'layer' => 57,
|
||||
'tl_schema' => [ // TL scheme files
|
||||
'layer' => 57, // layer version
|
||||
'src' => [
|
||||
'mtproto' => __DIR__.'/TL_mtproto_v1.json',
|
||||
'telegram' => __DIR__.'/TL_telegram_v57.json',
|
||||
'mtproto' => __DIR__.'/TL_mtproto_v1.json', // mtproto TL scheme
|
||||
'telegram' => __DIR__.'/TL_telegram_v57.json', // telegram TL scheme
|
||||
],
|
||||
],
|
||||
'logger' => [
|
||||
'logger' => 1,
|
||||
'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,
|
||||
'logger' => 3, // overwrite previous setting and echo logs
|
||||
],
|
||||
'max_tries' => [
|
||||
'query' => 5,
|
||||
'authorization' => 5,
|
||||
'response' => 5,
|
||||
'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' => [
|
||||
'msg_array_limit' => [ // How big should be the arrays containing the incoming and outgoing messages?
|
||||
'incoming' => 30,
|
||||
'outgoing' => 30,
|
||||
],
|
||||
@ -104,14 +144,21 @@ Slv8kg9qv1m6XHVQY3PnEw+QQtqSIXklHwIDAQAB
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isset($settings['connection_settings']['all'])) {
|
||||
foreach ($this->range(1, 6) as $n) {
|
||||
if (!isset($settings['connection_settings'][$n])) {
|
||||
$settings['connection_settings'][$n] = $settings['connection_settings']['all'];
|
||||
}
|
||||
}
|
||||
unset($settings['connection_settings']['all']);
|
||||
}
|
||||
$this->settings = $settings;
|
||||
|
||||
// Setup logger
|
||||
$this->setup_logger();
|
||||
|
||||
// Connect to servers
|
||||
\danog\MadelineProto\Logger::log('Istantiating DataCenter...');
|
||||
$this->datacenter = new DataCenter($this->settings['connection'], $this->settings['connection_settings']);
|
||||
$this->mk_datacenter();
|
||||
|
||||
// Load rsa key
|
||||
\danog\MadelineProto\Logger::log('Loading RSA key...');
|
||||
@ -121,13 +168,19 @@ Slv8kg9qv1m6XHVQY3PnEw+QQtqSIXklHwIDAQAB
|
||||
\danog\MadelineProto\Logger::log('Translating tl schemas...');
|
||||
$this->tl = new TL\TL($this->settings['tl_schema']['src']);
|
||||
|
||||
$this->incoming_messages = [];
|
||||
$this->outgoing_messages = [];
|
||||
$this->future_salts = [];
|
||||
|
||||
$this->switch_dc($this->settings['connection_settings']['default_dc'], true);
|
||||
$this->switch_dc(2, true);
|
||||
$this->get_config();
|
||||
}
|
||||
|
||||
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']);
|
||||
}
|
||||
|
||||
public function setup_logger()
|
||||
{
|
||||
if (!\danog\MadelineProto\Logger::$constructed) {
|
||||
@ -140,15 +193,18 @@ Slv8kg9qv1m6XHVQY3PnEw+QQtqSIXklHwIDAQAB
|
||||
public function switch_dc($new_dc, $allow_nearest_dc_switch = false)
|
||||
{
|
||||
\danog\MadelineProto\Logger::log('Switching to DC '.$new_dc.'...');
|
||||
/* if ($this->datacenter->curdc !== 0) {
|
||||
try {
|
||||
$exported_authorization = $this->method_call('auth.exportAuthorization', ['dc_id' => $new_dc]);
|
||||
} catch (\danog\MadelineProto\RPCErrorException $e) { ; }
|
||||
}*/
|
||||
if ($this->datacenter->curdc !== 0 && $this->datacenter->authorized) {
|
||||
$exported_authorization = $this->method_call('auth.exportAuthorization', ['dc_id' => $new_dc]);
|
||||
}
|
||||
if ($this->datacenter->dc_connect($new_dc)) {
|
||||
$this->init_authorization();
|
||||
$this->write_client_info($allow_nearest_dc_switch);
|
||||
// if (isset($exported_authorization)) $this->method_call('auth.importAuthorization', $exported_authorization);
|
||||
$this->config = $this->write_client_info('help.getConfig');
|
||||
$this->parse_config();
|
||||
if (isset($exported_authorization)) {
|
||||
$this->datacenter->authorization = $this->method_call('auth.importAuthorization', $exported_authorization);
|
||||
$this->datacenter->authorized = true;
|
||||
}
|
||||
$this->get_nearest_dc($allow_nearest_dc_switch);
|
||||
}
|
||||
}
|
||||
|
||||
@ -169,21 +225,24 @@ Slv8kg9qv1m6XHVQY3PnEw+QQtqSIXklHwIDAQAB
|
||||
}
|
||||
}
|
||||
|
||||
public function write_client_info($allow_switch)
|
||||
public function write_client_info($method, $arguments = [])
|
||||
{
|
||||
\danog\MadelineProto\Logger::log('Writing client info...');
|
||||
$nearest_dc = $this->method_call(
|
||||
\danog\MadelineProto\Logger::log('Writing client info (also executing '.$method.')...');
|
||||
return $this->method_call(
|
||||
'invokeWithLayer',
|
||||
[
|
||||
'layer' => $this->settings['tl_schema']['layer'],
|
||||
'query' => $this->tl->serialize_method('initConnection',
|
||||
array_merge(
|
||||
$this->settings['app_info'],
|
||||
['query' => $this->tl->serialize_method('help.getNearestDc', [])]
|
||||
['query' => $this->tl->serialize_method($method, $arguments)]
|
||||
)
|
||||
),
|
||||
]
|
||||
);
|
||||
}
|
||||
public function get_nearest_dc($allow_switch) {
|
||||
$nearest_dc = $this->method_call('help.getNearestDc');
|
||||
\danog\MadelineProto\Logger::log("We're in ".$nearest_dc['country'].', current dc is '.$nearest_dc['this_dc'].', nearest dc is '.$nearest_dc['nearest_dc'].'.');
|
||||
|
||||
if ($nearest_dc['nearest_dc'] != $nearest_dc['this_dc'] && $allow_switch) {
|
||||
@ -191,4 +250,23 @@ Slv8kg9qv1m6XHVQY3PnEw+QQtqSIXklHwIDAQAB
|
||||
$this->settings['connection_settings']['default_dc'] = $nearest_dc['nearest_dc'];
|
||||
}
|
||||
}
|
||||
|
||||
public function get_config() {
|
||||
if ($this->config['expires'] > time()) {
|
||||
return;
|
||||
}
|
||||
$this->config = $this->method_call('help.getConfig');
|
||||
$this->parse_config();
|
||||
}
|
||||
|
||||
public function parse_config() {
|
||||
\danog\MadelineProto\Logger::log('Received config!', $this->config);
|
||||
foreach ($this->config["dc_options"] as $dc) {
|
||||
$test = $this->config['test_mode'] ? 'test' : 'main';
|
||||
$ipv6 = ($dc['ipv6'] ? 'ipv6' : 'ipv4');
|
||||
$id = $dc['id'];
|
||||
$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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,22 +20,23 @@ class AckHandler extends \danog\MadelineProto\PrimeModule
|
||||
public function ack_outgoing_message_id($message_id)
|
||||
{
|
||||
// The server acknowledges that it received my message
|
||||
if (!isset($this->outgoing_messages[$message_id])) {
|
||||
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?');
|
||||
}
|
||||
$this->outgoing_messages[$message_id]['ack'] = true;
|
||||
$this->datacenter->outgoing_messages[$message_id]['ack'] = true;
|
||||
}
|
||||
|
||||
public function ack_incoming_message_id($message_id)
|
||||
{
|
||||
if ($this->datacenter->temp_auth_key['id'] === null || $this->datacenter->temp_auth_key['id'] == $this->string2bin('\x00\x00\x00\x00\x00\x00\x00\x00')) {
|
||||
return;
|
||||
}
|
||||
// I let the server know that I received its message
|
||||
if (!isset($this->incoming_messages[$message_id])) {
|
||||
if (!isset($this->datacenter->incoming_messages[$message_id])) {
|
||||
throw new \danog\MadelineProto\Exception("Couldn't find message id ".$message_id.' in the array of incoming message ids. Maybe try to increase its size?');
|
||||
}
|
||||
if ($this->datacenter->temp_auth_key['id'] === null || $this->datacenter->temp_auth_key['id'] == $this->string2bin('\x00\x00\x00\x00\x00\x00\x00\x00') || (isset($this->datacenter->incoming_messages[$message_id]['ack']) && $this->datacenter->incoming_messages[$message_id]['ack'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->object_call('msgs_ack', ['msg_ids' => [$message_id]]);
|
||||
$this->incoming_messages[$message_id]['ack'] = true;
|
||||
$this->datacenter->incoming_messages[$message_id]['ack'] = true;
|
||||
}
|
||||
}
|
||||
|
@ -318,7 +318,9 @@ class AuthKeyHandler extends AckHandler
|
||||
}
|
||||
|
||||
foreach ($this->range(0, $this->settings['max_tries']['authorization']) as $retry_id) {
|
||||
\danog\MadelineProto\Logger::log('Generating b...');
|
||||
$b = new \phpseclib\Math\BigInteger(\phpseclib\Crypt\Random::string(256), 256);
|
||||
\danog\MadelineProto\Logger::log('Generating g_b...');
|
||||
$g_b = $g->powMod($b, $dh_prime);
|
||||
|
||||
/*
|
||||
@ -467,8 +469,10 @@ class AuthKeyHandler extends AckHandler
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
} catch (\danog\MadelineProto\Exception $e) {
|
||||
\danog\MadelineProto\Logger::log('An exception occurred while generating the authorization key. Retrying...');
|
||||
} catch (\danog\MadelineProto\RPCErrorException $e) {
|
||||
\danog\MadelineProto\Logger::log('An RPCErrorExceptiom occurred while generating the authorization key. Retrying...');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -28,8 +28,8 @@ class CallHandler extends AuthKeyHandler
|
||||
\danog\MadelineProto\Logger::log('Getting response (try number '.$count.' for '.$optional_name.')...');
|
||||
$last_received = $this->recv_message();
|
||||
$this->handle_message($last_sent, $last_received);
|
||||
if (isset($this->outgoing_messages[$last_sent]['response']) && isset($this->incoming_messages[$this->outgoing_messages[$last_sent]['response']]['content'])) {
|
||||
$response = $this->incoming_messages[$this->outgoing_messages[$last_sent]['response']]['content'];
|
||||
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'];
|
||||
}
|
||||
}
|
||||
if ($response == null) {
|
||||
@ -43,7 +43,7 @@ class CallHandler extends AuthKeyHandler
|
||||
\danog\MadelineProto\Logger::log('Received request to switch to DC '.$dc);
|
||||
$this->switch_dc($dc);
|
||||
|
||||
return $this->method_call($this->outgoing_messages[$last_sent]['content']['method'], $this->outgoing_messages[$last_sent]['content']['args']);
|
||||
return $this->method_call($this->datacenter->outgoing_messages[$last_sent]['content']['method'], $this->datacenter->outgoing_messages[$last_sent]['content']['args']);
|
||||
|
||||
break;
|
||||
default:
|
||||
@ -57,29 +57,22 @@ class CallHandler extends AuthKeyHandler
|
||||
}
|
||||
}
|
||||
|
||||
public function method_call($method, $args, $message_id = null)
|
||||
public function method_call($method, $args = [], $message_id = null)
|
||||
{
|
||||
if (!is_array($args)) {
|
||||
throw new \danog\MadelineProto\Exception("Arguments aren't an array.");
|
||||
}
|
||||
foreach (range(1, $this->settings['max_tries']['query']) as $i) {
|
||||
foreach (range(1, $this->settings['max_tries']['query']) as $count) {
|
||||
try {
|
||||
\danog\MadelineProto\Logger::log('Calling method (try number '.$count.' for '.$method.')...');
|
||||
$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->outgoing_messages[$int_message_id]['content'] = ['method' => $method, 'args' => $args];
|
||||
$this->datacenter->outgoing_messages[$int_message_id]['content'] = ['method' => $method, 'args' => $args];
|
||||
$server_answer = $this->wait_for_response($int_message_id, $method);
|
||||
} 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();
|
||||
continue;
|
||||
} catch (\danog\MadelineProto\RPCErrorException $e) {
|
||||
if ($e->getCode() == -404) {
|
||||
\danog\MadelineProto\Logger::log('Temporary authorization key expired. Regenerating and recalling method...');
|
||||
$this->datacenter->temp_auth_key = null;
|
||||
$this->init_authorization();
|
||||
continue;
|
||||
}
|
||||
throw $e;
|
||||
}
|
||||
if ($server_answer == null) {
|
||||
throw new \danog\MadelineProto\Exception('An error occurred while calling method '.$method.'.');
|
||||
@ -90,16 +83,17 @@ class CallHandler extends AuthKeyHandler
|
||||
throw new \danog\MadelineProto\Exception('An error occurred while calling method '.$method.'.');
|
||||
}
|
||||
|
||||
public function object_call($object, $args)
|
||||
public function object_call($object, $args = [])
|
||||
{
|
||||
if (!is_array($args)) {
|
||||
throw new \danog\MadelineProto\Exception("Arguments aren't an array.");
|
||||
}
|
||||
|
||||
foreach (range(1, $this->settings['max_tries']['query']) as $i) {
|
||||
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));
|
||||
$this->outgoing_messages[$int_message_id]['content'] = ['object' => $object, 'args' => $args];
|
||||
$this->datacenter->outgoing_messages[$int_message_id]['content'] = ['object' => $object, 'args' => $args];
|
||||
// $server_answer = $this->wait_for_response($int_message_id);
|
||||
} 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...');
|
||||
|
@ -40,7 +40,7 @@ class MessageHandler extends Crypt
|
||||
$padding = \phpseclib\Crypt\Random::string($this->posmod(-strlen($encrypted_data), 16));
|
||||
list($aes_key, $aes_iv) = $this->aes_calculate($message_key, $this->datacenter->temp_auth_key['auth_key']);
|
||||
$message = $this->datacenter->temp_auth_key['id'].$message_key.$this->ige_encrypt($encrypted_data.$padding, $aes_key, $aes_iv);
|
||||
$this->outgoing_messages[$int_message_id]['seq_no'] = $seq_no;
|
||||
$this->datacenter->outgoing_messages[$int_message_id]['seq_no'] = $seq_no;
|
||||
}
|
||||
$this->datacenter->send_message($message);
|
||||
|
||||
@ -105,12 +105,12 @@ class MessageHandler extends Crypt
|
||||
if ($message_key != substr(sha1(substr($decrypted_data, 0, 32 + $message_data_length), true), -16)) {
|
||||
throw new \danog\MadelineProto\Exception('msg_key mismatch');
|
||||
}
|
||||
$this->incoming_messages[$message_id]['seq_no'] = $seq_no;
|
||||
$this->datacenter->incoming_messages[$message_id]['seq_no'] = $seq_no;
|
||||
} else {
|
||||
throw new \danog\MadelineProto\Exception('Got unknown auth_key id');
|
||||
}
|
||||
$deserialized = $this->tl->deserialize($this->fopen_and_write('php://memory', 'rw+b', $message_data));
|
||||
$this->incoming_messages[$message_id]['content'] = $deserialized;
|
||||
$this->datacenter->incoming_messages[$message_id]['content'] = $deserialized;
|
||||
|
||||
return $message_id;
|
||||
}
|
||||
|
@ -29,20 +29,20 @@ class MsgIdHandler extends MessageHandler
|
||||
if ($new_message_id % 4 != 0) {
|
||||
throw new \danog\MadelineProto\Exception('Given message id ('.$new_message_id.') is not divisible by 4.');
|
||||
}
|
||||
$keys = array_keys($this->outgoing_messages);
|
||||
$keys = array_keys($this->datacenter->outgoing_messages);
|
||||
asort($keys);
|
||||
if ($new_message_id <= end($keys)) {
|
||||
throw new \danog\MadelineProto\Exception('Given message id ('.$new_message_id.') is lower than or equal than the current limit ('.end($keys).').', 1);
|
||||
}
|
||||
$this->outgoing_messages[$new_message_id] = [];
|
||||
if (count($this->outgoing_messages) > $this->settings['msg_array_limit']['outgoing']) {
|
||||
array_shift($this->outgoing_messages);
|
||||
$this->datacenter->outgoing_messages[$new_message_id] = [];
|
||||
if (count($this->datacenter->outgoing_messages) > $this->settings['msg_array_limit']['outgoing']) {
|
||||
array_shift($this->datacenter->outgoing_messages);
|
||||
}
|
||||
} else {
|
||||
if ($new_message_id % 4 != 1 && $new_message_id % 4 != 3) {
|
||||
throw new \danog\MadelineProto\Exception('message id mod 4 != 1 or 3');
|
||||
}
|
||||
$keys = array_keys($this->incoming_messages);
|
||||
$keys = array_keys($this->datacenter->incoming_messages);
|
||||
if ($container) {
|
||||
asort($keys);
|
||||
if ($new_message_id >= end($keys)) {
|
||||
@ -56,11 +56,11 @@ class MsgIdHandler extends MessageHandler
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->incoming_messages[$new_message_id] = [];
|
||||
if (count($this->incoming_messages) > $this->settings['msg_array_limit']['incoming']) {
|
||||
array_shift($this->incoming_messages);
|
||||
$this->datacenter->incoming_messages[$new_message_id] = [];
|
||||
if (count($this->datacenter->incoming_messages) > $this->settings['msg_array_limit']['incoming']) {
|
||||
array_shift($this->datacenter->incoming_messages);
|
||||
}
|
||||
ksort($this->incoming_messages);
|
||||
ksort($this->datacenter->incoming_messages);
|
||||
}
|
||||
}
|
||||
|
||||
@ -75,7 +75,7 @@ class MsgIdHandler extends MessageHandler
|
||||
);
|
||||
*/
|
||||
|
||||
$keys = array_keys($this->outgoing_messages);
|
||||
$keys = array_keys($this->datacenter->outgoing_messages);
|
||||
asort($keys);
|
||||
$keys = end($keys);
|
||||
if ($int_message_id <= $keys) {
|
||||
|
@ -19,7 +19,8 @@ class ResponseHandler extends MsgIdHandler
|
||||
{
|
||||
public function handle_message($last_sent, $last_received)
|
||||
{
|
||||
$response = $this->incoming_messages[$last_received]['content'];
|
||||
$response = $this->datacenter->incoming_messages[$last_received]['content'];
|
||||
|
||||
switch ($response['_']) {
|
||||
case 'msgs_ack':
|
||||
foreach ($response['msg_ids'] as $msg_id) {
|
||||
@ -30,14 +31,15 @@ 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->outgoing_messages[$response['req_msg_id']]['response'] = $last_received;
|
||||
$this->incoming_messages[$last_received]['content'] = $response['result'];
|
||||
$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->outgoing_messages[$response['req_msg_id']]['response'] = $last_received;
|
||||
$this->incoming_messages[$last_received]['content'] = $response;
|
||||
$this->datacenter->outgoing_messages[$response['req_msg_id']]['response'] = $last_received;
|
||||
$this->datacenter->incoming_messages[$last_received]['content'] = $response;
|
||||
break;
|
||||
|
||||
case 'bad_msg_notification':
|
||||
@ -54,20 +56,20 @@ class ResponseHandler extends MsgIdHandler
|
||||
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.',
|
||||
];
|
||||
throw new \danog\MadelineProto\Exception('Received bad_msg_notification for '.$response['bad_msg_id'].': '.$error_codes[$response['error_code']]);
|
||||
throw new \danog\MadelineProto\RPCErrorException('Received bad_msg_notification for '.$response['bad_msg_id'].': '.$error_codes[$response['error_code']]);
|
||||
break;
|
||||
case 'bad_server_salt':
|
||||
$this->datacenter->temp_auth_key['server_salt'] = $response['new_server_salt'];
|
||||
$this->ack_outgoing_message_id($response['bad_msg_id']); // Acknowledge that the server received my request
|
||||
$this->outgoing_messages[$response['bad_msg_id']]['response'] = $last_received;
|
||||
$this->incoming_messages[$last_received]['content'] = $response;
|
||||
$this->datacenter->outgoing_messages[$response['bad_msg_id']]['response'] = $last_received;
|
||||
$this->datacenter->incoming_messages[$last_received]['content'] = $response;
|
||||
break;
|
||||
|
||||
case 'pong':
|
||||
foreach ($this->outgoing_messages as $msg_id => &$omessage) {
|
||||
foreach ($this->datacenter->outgoing_messages as $msg_id => &$omessage) {
|
||||
if (isset($omessage['content']['args']['ping_id']) && $omessage['content']['args']['ping_id'] == $response['ping_id']) {
|
||||
$omessage['response'] = $response['msg_id'];
|
||||
$this->incoming_messages[$response['msg_id']]['content'] = $response;
|
||||
$this->datacenter->incoming_messages[$response['msg_id']]['content'] = $response;
|
||||
$this->ack_outgoing_message_id($msg_id);
|
||||
}
|
||||
}
|
||||
@ -84,7 +86,7 @@ class ResponseHandler extends MsgIdHandler
|
||||
\danog\MadelineProto\Logger::log($response['messages']);
|
||||
foreach ($response['messages'] as $message) {
|
||||
$this->check_message_id($message['msg_id'], false, true);
|
||||
$this->incoming_messages[$message['msg_id']] = ['seq_no' => $message['seqno'], 'content' => $message['body']];
|
||||
$this->datacenter->incoming_messages[$message['msg_id']] = ['seq_no' => $message['seqno'], 'content' => $message['body']];
|
||||
$responses[] = $this->handle_message($last_sent, $message['msg_id']);
|
||||
}
|
||||
foreach ($responses as $key => $response) {
|
||||
@ -109,11 +111,11 @@ class ResponseHandler extends MsgIdHandler
|
||||
break;
|
||||
case 'msg_copy':
|
||||
$this->ack_incoming_message_id($last_received); // Acknowledge that I received the server's response
|
||||
if (isset($this->incoming_messages[$response['orig_message']['msg_id']])) {
|
||||
if (isset($this->datacenter->incoming_messages[$response['orig_message']['msg_id']])) {
|
||||
$this->ack_incoming_message_id($response['orig_message']['msg_id']); // Acknowledge that I received the server's response
|
||||
} else {
|
||||
$this->check_message_id($message['orig_message']['msg_id'], false, true);
|
||||
$this->incoming_messages[$message['orig_message']['msg_id']] = ['content' => $response['orig_message']];
|
||||
$this->datacenter->incoming_messages[$message['orig_message']['msg_id']] = ['content' => $response['orig_message']];
|
||||
|
||||
return $this->handle_message($last_sent, $message['orig_message']['msg_id']);
|
||||
}
|
||||
@ -123,8 +125,7 @@ class ResponseHandler extends MsgIdHandler
|
||||
\danog\MadelineProto\Logger::log($response);
|
||||
break;
|
||||
case 'gzip_packed':
|
||||
$this->incoming_messages[$last_received]['content'] = gzdecode($response);
|
||||
|
||||
$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':
|
||||
@ -132,8 +133,8 @@ class ResponseHandler extends MsgIdHandler
|
||||
$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->outgoing_messages[$last_sent]['response'] = $last_received;
|
||||
$this->incoming_messages[$last_received]['content'] = $response;
|
||||
$this->datacenter->outgoing_messages[$last_sent]['response'] = $last_received;
|
||||
$this->datacenter->incoming_messages[$last_received]['content'] = $response;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
17
src/danog/MadelineProto/RPCErrorException.php
Normal file
17
src/danog/MadelineProto/RPCErrorException.php
Normal file
@ -0,0 +1,17 @@
|
||||
<?php
|
||||
/*
|
||||
Copyright 2016 Daniil Gentili
|
||||
(https://daniil.it)
|
||||
This file is part of MadelineProto.
|
||||
MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
See the GNU Affero General Public License for more details.
|
||||
You should have received a copy of the GNU General Public License along with MadelineProto.
|
||||
If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto;
|
||||
|
||||
class RPCErrorException extends \Exception
|
||||
{
|
||||
}
|
@ -79,7 +79,7 @@ class TL extends \danog\MadelineProto\Tools
|
||||
$serialized = \danog\PHP\Struct::pack('<i', $tl['id']);
|
||||
$flags = 0;
|
||||
foreach ($tl['params'] as $cur_flag) {
|
||||
if ($cur_flag['opt']) {
|
||||
if ($cur_flag['flag']) {
|
||||
$flag_pow = pow(2, $cur_flag['pow']);
|
||||
switch ($cur_flag['type']) {
|
||||
case 'true':
|
||||
@ -102,7 +102,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['opt'] && (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 & pow(2, $current_argument['pow'])) == 0)) {
|
||||
//\danog\MadelineProto\Logger::log('Skipping '.$current_argument['name'].' of type '.$current_argument['type'].'/'.$current_argument['subtype']);
|
||||
continue;
|
||||
}
|
||||
@ -284,9 +284,8 @@ class TL extends \danog\MadelineProto\Tools
|
||||
$x = $this->deserialize($bytes_io, $tl_elem['predicate'], $subtype);
|
||||
} else {
|
||||
$x = ['_' => $tl_elem['predicate']];
|
||||
$done_opt = false;
|
||||
foreach ($tl_elem['params'] as $arg) {
|
||||
if ($arg['opt']) {
|
||||
if ($arg['flag']) {
|
||||
$flag_pow = pow(2, $arg['pow']);
|
||||
switch ($arg['type']) {
|
||||
case 'true':
|
||||
@ -309,6 +308,9 @@ class TL extends \danog\MadelineProto\Tools
|
||||
}
|
||||
$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;
|
||||
}
|
||||
|
@ -27,10 +27,10 @@ class TLConstructor
|
||||
$this->type[$this->key] = $json_dict['type'];
|
||||
$this->params[$this->key] = $json_dict['params'];
|
||||
foreach ($this->params[$this->key] as &$param) {
|
||||
$param['opt'] = false;
|
||||
$param['flag'] = false;
|
||||
$param['subtype'] = null;
|
||||
if (preg_match('/^flags\.\d*\?/', $param['type'])) {
|
||||
$param['opt'] = true;
|
||||
$param['flag'] = true;
|
||||
$param['pow'] = preg_replace(['/^flags\./', '/\?.*/'], '', $param['type']);
|
||||
$param['type'] = preg_replace('/^flags\.\d*\?/', '', $param['type']);
|
||||
}
|
||||
|
@ -33,10 +33,10 @@ class TLMethod
|
||||
}
|
||||
|
||||
foreach ($this->params[$this->key] as &$param) {
|
||||
$param['opt'] = false;
|
||||
$param['flag'] = false;
|
||||
$param['subtype'] = null;
|
||||
if (preg_match('/^flags\.\d*\?/', $param['type'])) {
|
||||
$param['opt'] = true;
|
||||
$param['flag'] = true;
|
||||
$param['pow'] = preg_replace(['/^flags\./', '/\?.*/'], '', $param['type']);
|
||||
$param['type'] = preg_replace('/^flags\.\d*\?/', '', $param['type']);
|
||||
}
|
||||
|
33
testing.php
33
testing.php
@ -14,41 +14,28 @@ 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')) {
|
||||
include_once 'number.php';
|
||||
$sentCode = $MadelineProto->auth->sendCode(
|
||||
|
||||
$checkedPhone = $MadelineProto->auth->checkPhone( // auth.checkPhone becomes auth->checkPhone
|
||||
[
|
||||
'phone_number' => $number,
|
||||
'sms_type' => 5,
|
||||
'api_id' => $MadelineProto->API->settings['app_info']['api_id'],
|
||||
'api_hash' => $MadelineProto->API->settings['app_info']['api_hash'],
|
||||
'lang_code' => $MadelineProto->API->settings['app_info']['lang_code'],
|
||||
'phone_number' => $number
|
||||
]
|
||||
);
|
||||
var_dump($checkedPhone);
|
||||
$sentCode = $MadelineProto->phone_login($number);
|
||||
var_dump($sentCode);
|
||||
echo 'Enter the code you received: ';
|
||||
$code = '';
|
||||
for ($x = 0; $x < $sentCode['type']['length']; $x++) {
|
||||
$code .= fgetc(STDIN);
|
||||
}
|
||||
$authorization = $MadelineProto->auth->signIn(
|
||||
[
|
||||
'phone_number' => $number,
|
||||
'phone_code_hash' => $sentCode['phone_code_hash'],
|
||||
'phone_code' => $code,
|
||||
]
|
||||
);
|
||||
$authorization = $MadelineProto->complete_phone_login($code);
|
||||
var_dump($authorization);
|
||||
}*/
|
||||
}
|
||||
if (file_exists('token.php')) {
|
||||
include_once 'token.php';
|
||||
$botauthorization = $MadelineProto->auth->importBotAuthorization(
|
||||
[
|
||||
'bot_auth_token' => $token,
|
||||
'api_id' => $MadelineProto->API->settings['app_info']['api_id'],
|
||||
'api_hash' => $MadelineProto->API->settings['app_info']['api_hash'],
|
||||
]
|
||||
);
|
||||
var_dump($botauthorization);
|
||||
$MadelineProto->bot_login($token);
|
||||
}
|
||||
echo 'Size of MadelineProto instance is '.strlen(var_export($MadelineProto, true)).' bytes'.PHP_EOL;
|
||||
|
Loading…
Reference in New Issue
Block a user