Multiple async and VoIP improvements

This commit is contained in:
Daniil Gentili 2019-03-29 20:25:42 +01:00
parent e5798a41db
commit 7005cdf10a
12 changed files with 158 additions and 325 deletions

2
docs

@ -1 +1 @@
Subproject commit 3dbba4bc60dc830c08d5343e438bcd003ba9d1c5 Subproject commit f0229af1baa48d7529f90d7c3dcfbf9d5f54187b

View File

@ -45,14 +45,6 @@ class EventHandler extends \danog\MadelineProto\EventHandler
$call->configuration['enable_NS'] = false; $call->configuration['enable_NS'] = false;
$call->configuration['enable_AGC'] = false; $call->configuration['enable_AGC'] = false;
$call->configuration['enable_AEC'] = false; $call->configuration['enable_AEC'] = false;
$call->configuration['shared_config'] = [
'audio_init_bitrate' => 100 * 1000,
'audio_max_bitrate' => 100 * 1000,
'audio_min_bitrate' => 10 * 1000,
'audio_congestion_window' => 4 * 1024,
//'audio_bitrate_step_decr' => 0,
//'audio_bitrate_step_incr' => 2000,
];
$call->configuration['log_file_path'] = '/tmp/logs' . $call->getCallID()['id'] . '.log'; // Default is /dev/null $call->configuration['log_file_path'] = '/tmp/logs' . $call->getCallID()['id'] . '.log'; // Default is /dev/null
//$call->configuration["stats_dump_file_path"] = "/tmp/stats".$call->getCallID()['id'].".txt"; // Default is /dev/null //$call->configuration["stats_dump_file_path"] = "/tmp/stats".$call->getCallID()['id'].".txt"; // Default is /dev/null
$call->parseConfig(); $call->parseConfig();
@ -289,6 +281,15 @@ Propic art by @magnaluna on [deviantart](https://magnaluna.deviantart.com).", 'p
} }
} }
} }
\danog\MadelineProto\VoIPServerConfig::update(
[
'audio_init_bitrate' => 100 * 1000,
'audio_max_bitrate' => 100 * 1000,
'audio_min_bitrate' => 10 * 1000,
'audio_congestion_window' => 4 * 1024,
]
);
$MadelineProto = new \danog\MadelineProto\API('session.madeline', ['secret_chats' => ['accept_chats' => false], 'logger' => ['logger' => 3, 'logger_param' => getcwd() . '/MadelineProto.log']]); $MadelineProto = new \danog\MadelineProto\API('session.madeline', ['secret_chats' => ['accept_chats' => false], 'logger' => ['logger' => 3, 'logger_param' => getcwd() . '/MadelineProto.log']]);
$MadelineProto->start(); $MadelineProto->start();

View File

@ -212,12 +212,6 @@ image: https://docs.madelineproto.xyz/favicons/android-chrome-256x256.png
$'.$constructor.$layer.' = '.$params.'; $'.$constructor.$layer.' = '.$params.';
``` ```
[PWRTelegram](https://pwrtelegram.xyz) json-encoded version:
```
'.$pwr_params.'
```
Or, if you\'re into Lua: Or, if you\'re into Lua:

View File

@ -230,28 +230,6 @@ $MadelineProto->start();
$'.$type.' = $MadelineProto->'.$php_method.'(['.$params.']); $'.$type.' = $MadelineProto->'.$php_method.'(['.$params.']);
``` ```
### [PWRTelegram HTTP API](https://pwrtelegram.xyz) example (NOT FOR MadelineProto):
'.($bot ? '### As a bot:
POST/GET to `https://api.pwrtelegram.xyz/botTOKEN/madeline`
Parameters:
* method - '.$data['method'].'
* params - `{'.$json_params.'}`
' : '').'
### As a user:
POST/GET to `https://api.pwrtelegram.xyz/userTOKEN/'.$data['method'].'`
Parameters:
'.$pwr_params.'
Or, if you\'re into Lua: Or, if you\'re into Lua:
```lua ```lua

View File

@ -19,6 +19,7 @@
namespace danog\MadelineProto; namespace danog\MadelineProto;
use Amp\Loop;
use danog\MadelineProto\MTProtoTools\ReferenceDatabase; use danog\MadelineProto\MTProtoTools\ReferenceDatabase;
use danog\MadelineProto\Stream\MTProtoTransport\HttpsStream; use danog\MadelineProto\Stream\MTProtoTransport\HttpsStream;
use danog\MadelineProto\Stream\MTProtoTransport\HttpStream; use danog\MadelineProto\Stream\MTProtoTransport\HttpStream;
@ -147,6 +148,7 @@ class MTProto implements TLCallback
private $supportUser = 0; private $supportUser = 0;
public $referenceDatabase; public $referenceDatabase;
public $update_deferred; public $update_deferred;
public $phoneConfigWatcherId;
public function __magic_construct($settings = []) public function __magic_construct($settings = [])
{ {
@ -162,6 +164,9 @@ class MTProto implements TLCallback
if (!extension_loaded('json')) { if (!extension_loaded('json')) {
throw new Exception(['extension', 'json']); throw new Exception(['extension', 'json']);
} }
if (!extension_loaded('mbstring')) {
throw new Exception(['extension', 'mbstring']);
}
// Connect to servers // Connect to servers
$this->logger->logger(\danog\MadelineProto\Lang::$current_lang['inst_dc'], Logger::ULTRA_VERBOSE); $this->logger->logger(\danog\MadelineProto\Lang::$current_lang['inst_dc'], Logger::ULTRA_VERBOSE);
if (!isset($this->datacenter)) { if (!isset($this->datacenter)) {
@ -236,6 +241,12 @@ class MTProto implements TLCallback
if (!extension_loaded('xml')) { if (!extension_loaded('xml')) {
throw new Exception(['extension', 'xml']); throw new Exception(['extension', 'xml']);
} }
if (!extension_loaded('mbstring')) {
throw new Exception(['extension', 'mbstring']);
}
if (!extension_loaded('json')) {
throw new Exception(['extension', 'json']);
}
if (!isset($this->referenceDatabase)) { if (!isset($this->referenceDatabase)) {
$this->referenceDatabase = new ReferenceDatabase($this); $this->referenceDatabase = new ReferenceDatabase($this);
@ -394,6 +405,9 @@ class MTProto implements TLCallback
public function __destruct() public function __destruct()
{ {
if ($this->phoneConfigWatcherId) {
Loop::cancel($this->phoneConfigWatcherId);
}
if (\danog\MadelineProto\Magic::$has_thread && is_object(\Thread::getCurrentThread())) { if (\danog\MadelineProto\Magic::$has_thread && is_object(\Thread::getCurrentThread())) {
return; return;
} }
@ -825,6 +839,16 @@ class MTProto implements TLCallback
} }
yield $dcs; yield $dcs;
yield $this->init_authorization_async(); yield $this->init_authorization_async();
$this->phoneConfigWatcherId = Loop::repeat(24 * 3600 * 1000, function ($watcherId) {
if ($this->authorized === self::LOGGED_IN) {
$this->logger->logger("Fetching phone config...");
VoIPServerConfig::updateDefault(yield $this->method_call_async_read('phone.getCallConfig', [], ['datacenter' => $this->datacenter->curdc]));
} else {
$this->logger->logger("Not fetching phone config");
}
});
VoIPServerConfig::updateDefault(yield $this->method_call_async_read('phone.getCallConfig', [], ['datacenter' => $this->datacenter->curdc]));
$this->logger->logger("Started phone config fetcher");
} }
public function get_config($config = [], $options = []) public function get_config($config = [], $options = [])
@ -900,9 +924,13 @@ class MTProto implements TLCallback
} }
public function get_self() public function get_self()
{
return $this->wait($this->get_self_async());
}
public function get_self_async()
{ {
try { try {
$this->authorization = ['user' => $this->method_call('users.getUsers', ['id' => [['_' => 'inputUserSelf']]], ['datacenter' => $this->datacenter->curdc])[0]]; $this->authorization = ['user' => (yield $this->method_call_async_read('users.getUsers', ['id' => [['_' => 'inputUserSelf']]], ['datacenter' => $this->datacenter->curdc]))[0]];
} catch (RPCErrorException $e) { } catch (RPCErrorException $e) {
$this->logger->logger($e->getMessage()); $this->logger->logger($e->getMessage());

View File

@ -463,11 +463,15 @@ trait AuthKeyHandler
} }
public function get_dh_config() public function get_dh_config()
{
return $this->wait($this->get_dh_config_async());
}
public function get_dh_config_async()
{ {
$this->updates_state['sync_loading'] = true; $this->updates_state['sync_loading'] = true;
try { try {
$dh_config = $this->method_call('messages.getDhConfig', ['version' => $this->dh_config['version'], 'random_length' => 0], ['datacenter' => $this->datacenter->curdc]); $dh_config = yield $this->method_call_async_read('messages.getDhConfig', ['version' => $this->dh_config['version'], 'random_length' => 0], ['datacenter' => $this->datacenter->curdc]);
} finally { } finally {
$this->updates_state['sync_loading'] = false; $this->updates_state['sync_loading'] = false;
} }

View File

@ -30,191 +30,6 @@ use function Amp\Promise\all;
*/ */
trait CallHandler trait CallHandler
{ {
/*
public function select()
{
$result = [];
try {
/*
if ($this->is_http($this->settings['connection_settings']['default_dc']) || $this->altervista) {
$this->logger->logger("Initial HTTP short poll");
$waiting = $this->datacenter->select(0.1);
$result = $this->handle_select($waiting, $result);
}/
$tries = 10; // TODO add setting
$this->logger->logger('Long poll');
$t = microtime(true);
$waiting = $this->datacenter->select();
$t = microtime(true) - $t;
$this->logger->logger("Long poll took $t");
$result = $this->handle_select($waiting, $result);
do {
$this->logger->logger('Short poll');
$waiting = $this->datacenter->select($this->is_http($this->settings['connection_settings']['default_dc']) || $this->altervista ? $this->settings['connection_settings']['all']['timeout'] / 10 : true);
$result = $this->handle_select($waiting, $result);
} while ($tries-- && $waiting);
} catch (\danog\MadelineProto\NothingInTheSocketException $e) {
$this->logger->logger('Nothing in the socket while selecting', \danog\MadelineProto\Logger::ULTRA_VERBOSE);
foreach ($this->datacenter->sockets as $dc => $socket) {
$this->close_and_reopen($dc);
$this->send_messages($dc);
}
}
return $result;
}
public $did = [];
public function handle_select($waiting, $result)
{
foreach ($waiting as $dc) {
$error = $this->recv_message($dc);
if ($error !== true) {
$this->close_and_reopen($dc);
if ($error === -404) {
if ($this->datacenter->sockets[$dc]->temp_auth_key !== null) {
$this->logger->logger('WARNING: Resetting auth key...', \danog\MadelineProto\Logger::WARNING);
$this->datacenter->sockets[$dc]->temp_auth_key = null;
$this->init_authorization();
return $result;
}
}
throw new \danog\MadelineProto\RPCErrorException($error, $error);
}
$result[$dc] = $this->handle_messages($dc) && (isset($result[$dc]) ? $result[$dc] : true);
if (($this->is_http($dc) || $this->altervista) && $this->datacenter->sockets[$dc]->new_outgoing) {
$this->send_messages($dc);
}
}
return $result;
}
public function iorun($updates)
{
do {
if ($updates && time() - $this->last_getdifference > $this->settings['updates']['getdifference_interval']) {
$this->get_updates_difference();
return;
}
if ($canunset = !$this->updates_state['sync_loading']) {
$this->updates_state['sync_loading'] = true;
}
if ($canunsetpostponeupdates = !$this->postpone_updates) {
$this->postpone_updates = true;
}
if ($canunsetpostponepwrchat = !$this->postpone_pwrchat) {
$this->postpone_pwrchat = true;
}
if (($this->is_http($this->settings['connection_settings']['default_dc']) || $this->altervista) && $updates) {
$this->send_messages($this->settings['connection_settings']['default_dc']);
}
foreach ($this->datacenter->sockets as $id => $datacenter) {
if ($datacenter->pending_outgoing) {
$this->send_messages($id);
}
}
$this->logger->logger('Polling for ' . ($updates ? 'updates' : 'replies') . ': selecting', \danog\MadelineProto\Logger::ULTRA_VERBOSE);
$t = microtime(true);
$only_updates = $this->select();
$t = microtime(true) - $t;
$this->logger->logger('Polling for ' . ($updates ? 'updates' : 'replies') . ': selecting took ' . $t, \danog\MadelineProto\Logger::ULTRA_VERBOSE);
$response_result = $this->has_pending_calls();
$repeat = 0;
foreach ($this->datacenter->sockets as $id => $datacenter) {
if ($updates) {
if (isset($only_updates[$id])) {
if ($only_updates[$id]) {
$this->logger->logger("Polling for updates: got only updates for DC $id", \danog\MadelineProto\Logger::VERBOSE);
} else {
$this->logger->logger("Polling for updates: got also RPC replies for DC $id", \danog\MadelineProto\Logger::NOTICE);
}
if ($response_result[$id]) {
$this->logger->logger("Polling for updates: still pending requests, resending for DC $id", \danog\MadelineProto\Logger::WARNING);
$this->send_messages($id);
}
} else {
if ($response_result[$id] || $id === $this->settings['connection_settings']['default_dc']) {
$this->logger->logger("Polling for updates: got nothing for DC $id", \danog\MadelineProto\Logger::ERROR);
if ($this->is_http($id) || $this->altervista) {
$this->logger->logger("Polling for updates: closing and reopening DC $id since we're on HTTP, and we polled");
$this->close_and_reopen($id);
$datacenter->last_http_wait = 0;
$repeat |= 1;
$this->logger->logger("Polling for updates: and now repolling for DC $id");
$this->send_messages($id);
}
}
}
} else {
if (isset($only_updates[$id])) {
if ($only_updates[$id]) {
$this->logger->logger("Polling for replies: got only updates for DC $id", \danog\MadelineProto\Logger::WARNING);
if ($response_result[$id]) {
$this->logger->logger("Polling for replies: still pending requests, repolling for DC $id", \danog\MadelineProto\Logger::WARNING);
$this->send_messages($id);
$repeat |= 1;
} else {
$this->logger->logger("Polling for replies: got all RPC replies for DC $id", \danog\MadelineProto\Logger::ULTRA_VERBOSE);
}
} else {
if ($response_result[$id]) {
$this->logger->logger("Polling for replies: still pending requests, repolling for DC $id", \danog\MadelineProto\Logger::WARNING);
$this->send_messages($id);
} else {
$this->logger->logger("Polling for replies: got all RPC replies for DC $id", \danog\MadelineProto\Logger::NOTICE);
}
}
} else {
if ($response_result[$id]) {
$this->logger->logger("Polling for replies: got nothing for DC $id", \danog\MadelineProto\Logger::ERROR);
$this->logger->logger("Polling for replies: closing and reopening DC $id", \danog\MadelineProto\Logger::ERROR);
$this->close_and_reopen($id);
$datacenter->last_http_wait = 0;
$repeat |= 1;
$this->logger->logger("Polling for replies: resending for DC $id", \danog\MadelineProto\Logger::WARNING);
$this->send_messages($id);
} else {
$this->logger->logger("Polling for replies: got all RPC replies for DC $id", \danog\MadelineProto\Logger::ULTRA_VERBOSE);
}
}
}
}
$this->logger->logger('Running guzzle promise queue');
\GuzzleHttp\Promise\queue()->run();
if ($repeat) {
$this->logger->logger('Repeat iowait');
}
} while ($repeat);
if ($canunset) {
$this->updates_state['sync_loading'] = false;
}
if ($canunsetpostponepwrchat) {
$this->postpone_pwrchat = false;
$this->handle_pending_pwrchat();
}
if ($canunsetpostponeupdates) {
$this->postpone_updates = false;
$this->handle_pending_updates();
}
}
*/
public function has_pending_calls() public function has_pending_calls()
{ {
$result = []; $result = [];

View File

@ -337,9 +337,12 @@ trait ResponseHandler
case 500: case 500:
if ($response['error_message'] === 'MSG_WAIT_FAILED') { if ($response['error_message'] === 'MSG_WAIT_FAILED') {
$this->datacenter->sockets[$datacenter]->call_queue[$request['queue']] = []; $this->datacenter->sockets[$datacenter]->call_queue[$request['queue']] = [];
}
Loop::defer([$this, 'method_recall'], ['message_id' => $request_id, 'datacenter' => $datacenter]); Loop::defer([$this, 'method_recall'], ['message_id' => $request_id, 'datacenter' => $datacenter]);
return;
}
$this->got_response_for_outgoing_message_id($request_id, $datacenter);
$this->handle_reject($datacenter, $request, new \danog\MadelineProto\RPCErrorException($response['error_message'], $response['error_code']));
return; return;
case 303: case 303:

View File

@ -55,7 +55,6 @@ trait UpdateHandler
return; return;
} }
$this->updates[$this->updates_key++] = $update; $this->updates[$this->updates_key++] = $update;
$this->logger->logger('Stored ');
} }
public function get_updates_async($params = []) public function get_updates_async($params = [])
@ -543,7 +542,8 @@ trait UpdateHandler
if (isset($this->calls[$update['phone_call']['id']])) { if (isset($this->calls[$update['phone_call']['id']])) {
return; return;
} }
$controller = new \danog\MadelineProto\VoIP(false, $update['phone_call']['admin_id'], ['_' => 'inputPhoneCall', 'id' => $update['phone_call']['id'], 'access_hash' => $update['phone_call']['access_hash']], $this, \danog\MadelineProto\VoIP::CALL_STATE_INCOMING, $update['phone_call']['protocol']); $controller = new \danog\MadelineProto\VoIP(false, $update['phone_call']['admin_id'], $this, \danog\MadelineProto\VoIP::CALL_STATE_INCOMING);
$controller->setCall($update['phone_call']);
$controller->storage = ['g_a_hash' => $update['phone_call']['g_a_hash']]; $controller->storage = ['g_a_hash' => $update['phone_call']['g_a_hash']];
$update['phone_call'] = $this->calls[$update['phone_call']['id']] = $controller; $update['phone_call'] = $this->calls[$update['phone_call']['id']] = $controller;
break; break;

View File

@ -51,9 +51,11 @@ trait AuthKeyHandler
$this->logger->logger(\danog\MadelineProto\Lang::$current_lang['generating_g_a'], \danog\MadelineProto\Logger::VERBOSE); $this->logger->logger(\danog\MadelineProto\Lang::$current_lang['generating_g_a'], \danog\MadelineProto\Logger::VERBOSE);
$g_a = $dh_config['g']->powMod($a, $dh_config['p']); $g_a = $dh_config['g']->powMod($a, $dh_config['p']);
$this->check_G($g_a, $dh_config['p']); $this->check_G($g_a, $dh_config['p']);
$res = $this->method_call('phone.requestCall', ['user_id' => $user, 'g_a_hash' => hash('sha256', $g_a->toBytes(), true), 'protocol' => ['_' => 'phoneCallProtocol', 'udp_p2p' => true, 'udp_reflector' => true, 'min_layer' => 65, 'max_layer' => 65]], ['datacenter' => $this->datacenter->curdc]); $controller = new \danog\MadelineProto\VoIP(true, $user['user_id'], $this, \danog\MadelineProto\VoIP::CALL_STATE_REQUESTED);
$this->calls[$res['phone_call']['id']] = $controller = new \danog\MadelineProto\VoIP(true, $user['user_id'], ['_' => 'inputPhoneCall', 'id' => $res['phone_call']['id'], 'access_hash' => $res['phone_call']['access_hash']], $this, \danog\MadelineProto\VoIP::CALL_STATE_REQUESTED, $res['phone_call']['protocol']);
$controller->storage = ['a' => $a, 'g_a' => str_pad($g_a->toBytes(), 256, chr(0), \STR_PAD_LEFT)]; $controller->storage = ['a' => $a, 'g_a' => str_pad($g_a->toBytes(), 256, chr(0), \STR_PAD_LEFT)];
$res = $this->method_call('phone.requestCall', ['user_id' => $user, 'g_a_hash' => hash('sha256', $g_a->toBytes(), true), 'protocol' => ['_' => 'phoneCallProtocol', 'udp_p2p' => true, 'udp_reflector' => true, 'min_layer' => 65, 'max_layer' => \danog\MadelineProto\VoIP::getConnectionMaxLayer()]], ['datacenter' => $this->datacenter->curdc]);
$controller->setCall($res['phone_call']);
$this->calls[$res['phone_call']['id']] = $controller;
$this->handle_pending_updates(); $this->handle_pending_updates();
$this->get_updates_difference(); $this->get_updates_difference();
@ -83,7 +85,7 @@ trait AuthKeyHandler
$this->check_G($g_b, $dh_config['p']); $this->check_G($g_b, $dh_config['p']);
try { try {
$res = $this->method_call('phone.acceptCall', ['peer' => $call, 'g_b' => $g_b->toBytes(), 'protocol' => ['_' => 'phoneCallProtocol', 'udp_reflector' => true, 'udp_p2p' => true, 'min_layer' => 65, 'max_layer' => 65]], ['datacenter' => $this->datacenter->curdc]); $res = $this->method_call('phone.acceptCall', ['peer' => $call, 'g_b' => $g_b->toBytes(), 'protocol' => ['_' => 'phoneCallProtocol', 'udp_reflector' => true, 'udp_p2p' => true, 'min_layer' => 65, 'max_layer' => \danog\MadelineProto\VoIP::getConnectionMaxLayer()]], ['datacenter' => $this->datacenter->curdc]);
} catch (\danog\MadelineProto\RPCErrorException $e) { } catch (\danog\MadelineProto\RPCErrorException $e) {
if ($e->rpc === 'CALL_ALREADY_ACCEPTED') { if ($e->rpc === 'CALL_ALREADY_ACCEPTED') {
$this->logger->logger(sprintf(\danog\MadelineProto\Lang::$current_lang['call_already_accepted'], $call['id'])); $this->logger->logger(sprintf(\danog\MadelineProto\Lang::$current_lang['call_already_accepted'], $call['id']));
@ -126,7 +128,7 @@ trait AuthKeyHandler
$params['g_b'] = new \phpseclib\Math\BigInteger($params['g_b'], 256); $params['g_b'] = new \phpseclib\Math\BigInteger($params['g_b'], 256);
$this->check_G($params['g_b'], $dh_config['p']); $this->check_G($params['g_b'], $dh_config['p']);
$key = str_pad($params['g_b']->powMod($this->calls[$params['id']]->storage['a'], $dh_config['p'])->toBytes(), 256, chr(0), \STR_PAD_LEFT); $key = str_pad($params['g_b']->powMod($this->calls[$params['id']]->storage['a'], $dh_config['p'])->toBytes(), 256, chr(0), \STR_PAD_LEFT);
$res = $this->method_call('phone.confirmCall', ['key_fingerprint' => substr(sha1($key, true), -8), 'peer' => ['id' => $params['id'], 'access_hash' => $params['access_hash'], '_' => 'inputPhoneCall'], 'g_a' => $this->calls[$params['id']]->storage['g_a'], 'protocol' => ['_' => 'phoneCallProtocol', 'udp_reflector' => true, 'min_layer' => 65, 'max_layer' => 65]], ['datacenter' => $this->datacenter->curdc])['phone_call']; $res = $this->method_call('phone.confirmCall', ['key_fingerprint' => substr(sha1($key, true), -8), 'peer' => ['id' => $params['id'], 'access_hash' => $params['access_hash'], '_' => 'inputPhoneCall'], 'g_a' => $this->calls[$params['id']]->storage['g_a'], 'protocol' => ['_' => 'phoneCallProtocol', 'udp_reflector' => true, 'min_layer' => 65, 'max_layer' => \danog\MadelineProto\VoIP::getConnectionMaxLayer()]], ['datacenter' => $this->datacenter->curdc])['phone_call'];
$visualization = []; $visualization = [];
$length = new \phpseclib\Math\BigInteger(count(\danog\MadelineProto\Magic::$emojis)); $length = new \phpseclib\Math\BigInteger(count(\danog\MadelineProto\Magic::$emojis));
foreach (str_split(hash('sha256', $key.str_pad($this->calls[$params['id']]->storage['g_a'], 256, chr(0), \STR_PAD_LEFT), true), 8) as $number) { foreach (str_split(hash('sha256', $key.str_pad($this->calls[$params['id']]->storage['g_a'], 256, chr(0), \STR_PAD_LEFT), true), 8) as $number) {
@ -134,7 +136,7 @@ trait AuthKeyHandler
$visualization[] = \danog\MadelineProto\Magic::$emojis[(int) (new \phpseclib\Math\BigInteger($number, 256))->divide($length)[1]->toString()]; $visualization[] = \danog\MadelineProto\Magic::$emojis[(int) (new \phpseclib\Math\BigInteger($number, 256))->divide($length)[1]->toString()];
} }
$this->calls[$params['id']]->setVisualization($visualization); $this->calls[$params['id']]->setVisualization($visualization);
$this->calls[$params['id']]->configuration['shared_config'] = array_merge($this->method_call('phone.getCallConfig', [], ['datacenter' => $this->datacenter->curdc]), $this->calls[$params['id']]->configuration['shared_config']);
$this->calls[$params['id']]->configuration['endpoints'] = array_merge([$res['connection']], $res['alternative_connections'], $this->calls[$params['id']]->configuration['endpoints']); $this->calls[$params['id']]->configuration['endpoints'] = array_merge([$res['connection']], $res['alternative_connections'], $this->calls[$params['id']]->configuration['endpoints']);
$this->calls[$params['id']]->configuration = array_merge(['recv_timeout' => $this->config['call_receive_timeout_ms'] / 1000, 'init_timeout' => $this->config['call_connect_timeout_ms'] / 1000, 'data_saving' => \danog\MadelineProto\VoIP::DATA_SAVING_NEVER, 'enable_NS' => true, 'enable_AEC' => true, 'enable_AGC' => true, 'auth_key' => $key, 'auth_key_id' => substr(sha1($key, true), -8), 'call_id' => substr(hash('sha256', $key, true), -16), 'network_type' => \danog\MadelineProto\VoIP::NET_TYPE_ETHERNET], $this->calls[$params['id']]->configuration); $this->calls[$params['id']]->configuration = array_merge(['recv_timeout' => $this->config['call_receive_timeout_ms'] / 1000, 'init_timeout' => $this->config['call_connect_timeout_ms'] / 1000, 'data_saving' => \danog\MadelineProto\VoIP::DATA_SAVING_NEVER, 'enable_NS' => true, 'enable_AEC' => true, 'enable_AGC' => true, 'auth_key' => $key, 'auth_key_id' => substr(sha1($key, true), -8), 'call_id' => substr(hash('sha256', $key, true), -16), 'network_type' => \danog\MadelineProto\VoIP::NET_TYPE_ETHERNET], $this->calls[$params['id']]->configuration);
$this->calls[$params['id']]->parseConfig(); $this->calls[$params['id']]->parseConfig();
@ -177,7 +179,6 @@ trait AuthKeyHandler
$visualization[] = \danog\MadelineProto\Magic::$emojis[(int) (new \phpseclib\Math\BigInteger($number, 256))->divide($length)[1]->toString()]; $visualization[] = \danog\MadelineProto\Magic::$emojis[(int) (new \phpseclib\Math\BigInteger($number, 256))->divide($length)[1]->toString()];
} }
$this->calls[$params['id']]->setVisualization($visualization); $this->calls[$params['id']]->setVisualization($visualization);
$this->calls[$params['id']]->configuration['shared_config'] = array_merge($this->method_call('phone.getCallConfig', [], ['datacenter' => $this->datacenter->curdc]), $this->calls[$params['id']]->configuration['shared_config']);
$this->calls[$params['id']]->configuration['endpoints'] = array_merge([$params['connection']], $params['alternative_connections'], $this->calls[$params['id']]->configuration['endpoints']); $this->calls[$params['id']]->configuration['endpoints'] = array_merge([$params['connection']], $params['alternative_connections'], $this->calls[$params['id']]->configuration['endpoints']);
$this->calls[$params['id']]->configuration = array_merge(['recv_timeout' => $this->config['call_receive_timeout_ms'] / 1000, 'init_timeout' => $this->config['call_connect_timeout_ms'] / 1000, 'data_saving' => \danog\MadelineProto\VoIP::DATA_SAVING_NEVER, 'enable_NS' => true, 'enable_AEC' => true, 'enable_AGC' => true, 'auth_key' => $key, 'auth_key_id' => substr(sha1($key, true), -8), 'call_id' => substr(hash('sha256', $key, true), -16), 'network_type' => \danog\MadelineProto\VoIP::NET_TYPE_ETHERNET], $this->calls[$params['id']]->configuration); $this->calls[$params['id']]->configuration = array_merge(['recv_timeout' => $this->config['call_receive_timeout_ms'] / 1000, 'init_timeout' => $this->config['call_connect_timeout_ms'] / 1000, 'data_saving' => \danog\MadelineProto\VoIP::DATA_SAVING_NEVER, 'enable_NS' => true, 'enable_AEC' => true, 'enable_AGC' => true, 'auth_key' => $key, 'auth_key_id' => substr(sha1($key, true), -8), 'call_id' => substr(hash('sha256', $key, true), -16), 'network_type' => \danog\MadelineProto\VoIP::NET_TYPE_ETHERNET], $this->calls[$params['id']]->configuration);
$this->calls[$params['id']]->parseConfig(); $this->calls[$params['id']]->parseConfig();

View File

@ -20,6 +20,8 @@
namespace danog\MadelineProto\Wrappers; namespace danog\MadelineProto\Wrappers;
use danog\MadelineProto\MTProtoTools\PasswordCalculator; use danog\MadelineProto\MTProtoTools\PasswordCalculator;
use danog\MadelineProto\VoIPServerConfig;
use function Amp\Promise\wait;
/** /**
* Manages logging in and out. * Manages logging in and out.
@ -27,6 +29,10 @@ use danog\MadelineProto\MTProtoTools\PasswordCalculator;
trait Login trait Login
{ {
public function logout() public function logout()
{
return $this->wait($this->logout_async());
}
public function logout_async()
{ {
foreach ($this->datacenter->sockets as $socket) { foreach ($this->datacenter->sockets as $socket) {
$socket->authorized = false; $socket->authorized = false;
@ -40,41 +46,39 @@ trait Login
$this->users = []; $this->users = [];
$this->state = []; $this->state = [];
$this->tos = ['expires' => 0, 'accepted' => true]; $this->tos = ['expires' => 0, 'accepted' => true];
if (!$this->method_call('auth.logOut', [], ['datacenter' => $this->datacenter->curdc])) { yield $this->method_call_async_read('auth.logOut', [], ['datacenter' => $this->datacenter->curdc]);
throw new \danog\MadelineProto\Exception(\danog\MadelineProto\Lang::$current_lang['logout_error']);
}
$this->logger->logger(\danog\MadelineProto\Lang::$current_lang['logout_ok'], \danog\MadelineProto\Logger::NOTICE); $this->logger->logger(\danog\MadelineProto\Lang::$current_lang['logout_ok'], \danog\MadelineProto\Logger::NOTICE);
return true; return true;
} }
public function bot_login($token) public function bot_login_async($token)
{ {
if ($this->authorized === self::LOGGED_IN) { if ($this->authorized === self::LOGGED_IN) {
$this->logger->logger(\danog\MadelineProto\Lang::$current_lang['already_logged_in'], \danog\MadelineProto\Logger::NOTICE); $this->logger->logger(\danog\MadelineProto\Lang::$current_lang['already_logged_in'], \danog\MadelineProto\Logger::NOTICE);
$this->logout(); yield $this->logout_async();
} }
$this->logger->logger(\danog\MadelineProto\Lang::$current_lang['login_bot'], \danog\MadelineProto\Logger::NOTICE); $this->logger->logger(\danog\MadelineProto\Lang::$current_lang['login_bot'], \danog\MadelineProto\Logger::NOTICE);
$this->authorization = $this->method_call('auth.importBotAuthorization', ['bot_auth_token' => $token, 'api_id' => $this->settings['app_info']['api_id'], 'api_hash' => $this->settings['app_info']['api_hash']], ['datacenter' => $this->datacenter->curdc]); $this->authorization = yield $this->method_call_async_read('auth.importBotAuthorization', ['bot_auth_token' => $token, 'api_id' => $this->settings['app_info']['api_id'], 'api_hash' => $this->settings['app_info']['api_hash']], ['datacenter' => $this->datacenter->curdc]);
$this->authorized = self::LOGGED_IN; $this->authorized = self::LOGGED_IN;
$this->authorized_dc = $this->datacenter->curdc; $this->authorized_dc = $this->datacenter->curdc;
$this->datacenter->sockets[$this->datacenter->curdc]->authorized = true; $this->datacenter->sockets[$this->datacenter->curdc]->authorized = true;
$this->updates = []; $this->updates = [];
$this->updates_key = 0; $this->updates_key = 0;
$this->init_authorization(); yield $this->init_authorization_async();
$this->logger->logger(\danog\MadelineProto\Lang::$current_lang['login_ok'], \danog\MadelineProto\Logger::NOTICE); $this->logger->logger(\danog\MadelineProto\Lang::$current_lang['login_ok'], \danog\MadelineProto\Logger::NOTICE);
return $this->authorization; return $this->authorization;
} }
public function phone_login($number, $sms_type = 5) public function phone_login_async($number, $sms_type = 5)
{ {
if ($this->authorized === self::LOGGED_IN) { if ($this->authorized === self::LOGGED_IN) {
$this->logger->logger(\danog\MadelineProto\Lang::$current_lang['already_logged_in'], \danog\MadelineProto\Logger::NOTICE); $this->logger->logger(\danog\MadelineProto\Lang::$current_lang['already_logged_in'], \danog\MadelineProto\Logger::NOTICE);
$this->logout(); yield $this->logout_async();
} }
$this->logger->logger(\danog\MadelineProto\Lang::$current_lang['login_code_sending'], \danog\MadelineProto\Logger::NOTICE); $this->logger->logger(\danog\MadelineProto\Lang::$current_lang['login_code_sending'], \danog\MadelineProto\Logger::NOTICE);
$this->authorization = $this->method_call('auth.sendCode', ['settings' => ['_' => 'codeSettings'], 'phone_number' => $number, 'sms_type' => $sms_type, 'api_id' => $this->settings['app_info']['api_id'], 'api_hash' => $this->settings['app_info']['api_hash'], 'lang_code' => $this->settings['app_info']['lang_code']], ['datacenter' => $this->datacenter->curdc]); $this->authorization = yield $this->method_call_async_read('auth.sendCode', ['settings' => ['_' => 'codeSettings'], 'phone_number' => $number, 'sms_type' => $sms_type, 'api_id' => $this->settings['app_info']['api_id'], 'api_hash' => $this->settings['app_info']['api_hash'], 'lang_code' => $this->settings['app_info']['lang_code']], ['datacenter' => $this->datacenter->curdc]);
$this->authorized_dc = $this->datacenter->curdc; $this->authorized_dc = $this->datacenter->curdc;
$this->authorization['phone_number'] = $number; $this->authorization['phone_number'] = $number;
//$this->authorization['_'] .= 'MP'; //$this->authorization['_'] .= 'MP';
@ -86,7 +90,7 @@ trait Login
return $this->authorization; return $this->authorization;
} }
public function complete_phone_login($code) public function complete_phone_login_async($code)
{ {
if ($this->authorized !== self::WAITING_CODE) { if ($this->authorized !== self::WAITING_CODE) {
throw new \danog\MadelineProto\Exception(\danog\MadelineProto\Lang::$current_lang['login_code_uncalled']); throw new \danog\MadelineProto\Exception(\danog\MadelineProto\Lang::$current_lang['login_code_uncalled']);
@ -95,11 +99,11 @@ trait Login
$this->logger->logger(\danog\MadelineProto\Lang::$current_lang['login_user'], \danog\MadelineProto\Logger::NOTICE); $this->logger->logger(\danog\MadelineProto\Lang::$current_lang['login_user'], \danog\MadelineProto\Logger::NOTICE);
try { try {
$authorization = $this->method_call('auth.signIn', ['phone_number' => $this->authorization['phone_number'], 'phone_code_hash' => $this->authorization['phone_code_hash'], 'phone_code' => (string) $code], ['datacenter' => $this->datacenter->curdc]); $authorization = yield $this->method_call_async_read('auth.signIn', ['phone_number' => $this->authorization['phone_number'], 'phone_code_hash' => $this->authorization['phone_code_hash'], 'phone_code' => (string) $code], ['datacenter' => $this->datacenter->curdc]);
} catch (\danog\MadelineProto\RPCErrorException $e) { } catch (\danog\MadelineProto\RPCErrorException $e) {
if ($e->rpc === 'SESSION_PASSWORD_NEEDED') { if ($e->rpc === 'SESSION_PASSWORD_NEEDED') {
$this->logger->logger(\danog\MadelineProto\Lang::$current_lang['login_2fa_enabled'], \danog\MadelineProto\Logger::NOTICE); $this->logger->logger(\danog\MadelineProto\Lang::$current_lang['login_2fa_enabled'], \danog\MadelineProto\Logger::NOTICE);
$this->authorization = $this->method_call('account.getPassword', [], ['datacenter' => $this->datacenter->curdc]); $this->authorization = yield $this->method_call_async_read('account.getPassword', [], ['datacenter' => $this->datacenter->curdc]);
if (!isset($this->authorization['hint'])) $this->authorization['hint'] = ''; if (!isset($this->authorization['hint'])) $this->authorization['hint'] = '';
$this->authorized = self::WAITING_PASSWORD; $this->authorized = self::WAITING_PASSWORD;
@ -118,17 +122,19 @@ trait Login
$this->authorized = self::LOGGED_IN; $this->authorized = self::LOGGED_IN;
$this->authorization = $authorization; $this->authorization = $authorization;
$this->datacenter->sockets[$this->datacenter->curdc]->authorized = true; $this->datacenter->sockets[$this->datacenter->curdc]->authorized = true;
$this->init_authorization(); yield $this->init_authorization_async();
VoIPServerConfig::updateDefault(yield $this->method_call_async_read('phone.getCallConfig', [], ['datacenter' => $this->datacenter->curdc]));
$this->logger->logger(\danog\MadelineProto\Lang::$current_lang['login_ok'], \danog\MadelineProto\Logger::NOTICE); $this->logger->logger(\danog\MadelineProto\Lang::$current_lang['login_ok'], \danog\MadelineProto\Logger::NOTICE);
return $this->authorization; return $this->authorization;
} }
public function import_authorization($authorization) public function import_authorization_async($authorization)
{ {
if ($this->authorized === self::LOGGED_IN) { if ($this->authorized === self::LOGGED_IN) {
$this->logger->logger(\danog\MadelineProto\Lang::$current_lang['already_logged_in'], \danog\MadelineProto\Logger::NOTICE); $this->logger->logger(\danog\MadelineProto\Lang::$current_lang['already_logged_in'], \danog\MadelineProto\Logger::NOTICE);
$this->logout(); yield $this->logout_async();
} }
$this->logger->logger(\danog\MadelineProto\Lang::$current_lang['login_auth_key'], \danog\MadelineProto\Logger::NOTICE); $this->logger->logger(\danog\MadelineProto\Lang::$current_lang['login_auth_key'], \danog\MadelineProto\Logger::NOTICE);
list($dc_id, $auth_key) = $authorization; list($dc_id, $auth_key) = $authorization;
@ -147,39 +153,42 @@ trait Login
$this->datacenter->sockets[$dc_id]->new_incoming = []; $this->datacenter->sockets[$dc_id]->new_incoming = [];
$this->datacenter->sockets[$dc_id]->authorized = true; $this->datacenter->sockets[$dc_id]->authorized = true;
$this->authorized = self::LOGGED_IN; $this->authorized = self::LOGGED_IN;
$this->init_authorization(); yield $this->init_authorization_async();
VoIPServerConfig::updateDefault(yield $this->method_call_async_read('phone.getCallConfig', [], ['datacenter' => $this->datacenter->curdc]));
return $this->get_self(); return yield $this->get_self_async();
} }
public function export_authorization() public function export_authorization_async()
{ {
if ($this->authorized !== self::LOGGED_IN) { if ($this->authorized !== self::LOGGED_IN) {
throw new \danog\MadelineProto\Exception(\danog\MadelineProto\Lang::$current_lang['not_logged_in']); throw new \danog\MadelineProto\Exception(\danog\MadelineProto\Lang::$current_lang['not_logged_in']);
} }
$this->get_self(); yield $this->get_self_async();
$this->authorized_dc = $this->datacenter->curdc; $this->authorized_dc = $this->datacenter->curdc;
return [$this->datacenter->curdc, $this->datacenter->sockets[$this->datacenter->curdc]->auth_key['auth_key']]; return [$this->datacenter->curdc, $this->datacenter->sockets[$this->datacenter->curdc]->auth_key['auth_key']];
} }
public function complete_signup($first_name, $last_name) public function complete_signup_async($first_name, $last_name)
{ {
if ($this->authorized !== self::WAITING_SIGNUP) { if ($this->authorized !== self::WAITING_SIGNUP) {
throw new \danog\MadelineProto\Exception(\danog\MadelineProto\Lang::$current_lang['signup_uncalled']); throw new \danog\MadelineProto\Exception(\danog\MadelineProto\Lang::$current_lang['signup_uncalled']);
} }
$this->authorized = self::NOT_LOGGED_IN; $this->authorized = self::NOT_LOGGED_IN;
$this->logger->logger(\danog\MadelineProto\Lang::$current_lang['signing_up'], \danog\MadelineProto\Logger::NOTICE); $this->logger->logger(\danog\MadelineProto\Lang::$current_lang['signing_up'], \danog\MadelineProto\Logger::NOTICE);
$this->authorization = $this->method_call('auth.signUp', ['phone_number' => $this->authorization['phone_number'], 'phone_code_hash' => $this->authorization['phone_code_hash'], 'phone_code' => $this->authorization['phone_code'], 'first_name' => $first_name, 'last_name' => $last_name], ['datacenter' => $this->datacenter->curdc]); $this->authorization = yield $this->method_call_async_read('auth.signUp', ['phone_number' => $this->authorization['phone_number'], 'phone_code_hash' => $this->authorization['phone_code_hash'], 'phone_code' => $this->authorization['phone_code'], 'first_name' => $first_name, 'last_name' => $last_name], ['datacenter' => $this->datacenter->curdc]);
$this->authorized = self::LOGGED_IN; $this->authorized = self::LOGGED_IN;
$this->datacenter->sockets[$this->datacenter->curdc]->authorized = true; $this->datacenter->sockets[$this->datacenter->curdc]->authorized = true;
$this->init_authorization(); yield $this->init_authorization_async();
VoIPServerConfig::updateDefault(yield $this->method_call_async_read('phone.getCallConfig', [], ['datacenter' => $this->datacenter->curdc]));
$this->logger->logger(\danog\MadelineProto\Lang::$current_lang['signup_ok'], \danog\MadelineProto\Logger::NOTICE); $this->logger->logger(\danog\MadelineProto\Lang::$current_lang['signup_ok'], \danog\MadelineProto\Logger::NOTICE);
return $this->authorization; return $this->authorization;
} }
public function complete_2fa_login($password) public function complete_2fa_login_async($password)
{ {
if ($this->authorized !== self::WAITING_PASSWORD) { if ($this->authorized !== self::WAITING_PASSWORD) {
throw new \danog\MadelineProto\Exception(\danog\MadelineProto\Lang::$current_lang['2fa_uncalled']); throw new \danog\MadelineProto\Exception(\danog\MadelineProto\Lang::$current_lang['2fa_uncalled']);
@ -188,20 +197,21 @@ trait Login
$hasher = new PasswordCalculator($this->logger); $hasher = new PasswordCalculator($this->logger);
$hasher->addInfo($this->authorization); $hasher->addInfo($this->authorization);
$this->logger->logger(\danog\MadelineProto\Lang::$current_lang['login_user'], \danog\MadelineProto\Logger::NOTICE); $this->logger->logger(\danog\MadelineProto\Lang::$current_lang['login_user'], \danog\MadelineProto\Logger::NOTICE);
$this->authorization = $this->method_call('auth.checkPassword', ['password' => $hasher->getCheckPassword($password)], ['datacenter' => $this->datacenter->curdc]); $this->authorization = yield $this->method_call_async_read('auth.checkPassword', ['password' => $hasher->getCheckPassword($password)], ['datacenter' => $this->datacenter->curdc]);
$this->authorized = self::LOGGED_IN; $this->authorized = self::LOGGED_IN;
$this->datacenter->sockets[$this->datacenter->curdc]->authorized = true; $this->datacenter->sockets[$this->datacenter->curdc]->authorized = true;
$this->init_authorization(); yield $this->init_authorization_async();
$this->logger->logger(\danog\MadelineProto\Lang::$current_lang['login_ok'], \danog\MadelineProto\Logger::NOTICE); $this->logger->logger(\danog\MadelineProto\Lang::$current_lang['login_ok'], \danog\MadelineProto\Logger::NOTICE);
VoIPServerConfig::updateDefault(yield $this->method_call_async_read('phone.getCallConfig', [], ['datacenter' => $this->datacenter->curdc]));
return $this->authorization; return $this->authorization;
} }
public function update_2fa(array $params): bool public function update_2fa_async(array $params)
{ {
$hasher = new PasswordCalculator($this->logger); $hasher = new PasswordCalculator($this->logger);
$hasher->addInfo($this->method_call('account.getPassword', [], ['datacenter' => $this->datacenter->curdc])); $hasher->addInfo(yield $this->method_call_async_read('account.getPassword', [], ['datacenter' => $this->datacenter->curdc]));
return $this->method_call('account.updatePasswordSettings', $hasher->getPassword($params), ['datacenter' => $this->datacenter->curdc]); return yield $this->method_call_async_read('account.updatePasswordSettings', $hasher->getPassword($params), ['datacenter' => $this->datacenter->curdc]);
} }
} }

View File

@ -102,7 +102,6 @@ trait Loop
$this->datacenter->sockets[$this->settings['connection_settings']['default_dc']]->updater->start(); $this->datacenter->sockets[$this->settings['connection_settings']['default_dc']]->updater->start();
$this->logger->logger('Started update loop', \danog\MadelineProto\Logger::NOTICE); $this->logger->logger('Started update loop', \danog\MadelineProto\Logger::NOTICE);
$offset = 0;
while (true) { while (true) {
foreach ($this->updates as $update) { foreach ($this->updates as $update) {