Refactoring

This commit is contained in:
Daniil Gentili 2019-08-31 22:43:58 +02:00
parent d2010cc259
commit 3fe86e0162
20 changed files with 722 additions and 294 deletions

View File

@ -19,7 +19,7 @@
namespace phpseclib\Math; namespace phpseclib\Math;
if (PHP_MAJOR_VERSION < 7 && !(class_exists('\\Phar') && \Phar::running())) { if (PHP_MAJOR_VERSION < 7 && !(class_exists(\Phar::class) && \Phar::running())) {
throw new \Exception('MadelineProto requires php 7 to run natively, use phar.madelineproto.xyz to run on PHP 5.6'); throw new \Exception('MadelineProto requires php 7 to run natively, use phar.madelineproto.xyz to run on PHP 5.6');
} }
if (defined('HHVM_VERSION')) { if (defined('HHVM_VERSION')) {

View File

@ -24,10 +24,8 @@ use danog\MadelineProto\Loop\Connection\CheckLoop;
use danog\MadelineProto\Loop\Connection\HttpWaitLoop; use danog\MadelineProto\Loop\Connection\HttpWaitLoop;
use danog\MadelineProto\Loop\Connection\ReadLoop; use danog\MadelineProto\Loop\Connection\ReadLoop;
use danog\MadelineProto\Loop\Connection\WriteLoop; use danog\MadelineProto\Loop\Connection\WriteLoop;
use danog\MadelineProto\MTProtoTools\Crypt;
use danog\MadelineProto\Stream\ConnectionContext; use danog\MadelineProto\Stream\ConnectionContext;
use danog\MadelineProto\Stream\MTProtoTools\MsgIdHandler; use danog\MadelineProto\Stream\MTProtoTools\Session;
use danog\MadelineProto\Stream\MTProtoTools\SeqNoHandler;
/** /**
* Connection class. * Connection class.
@ -36,44 +34,176 @@ use danog\MadelineProto\Stream\MTProtoTools\SeqNoHandler;
* *
* @author Daniil Gentili <daniil@daniil.it> * @author Daniil Gentili <daniil@daniil.it>
*/ */
class Connection class Connection extends Session
{ {
use Crypt;
use MsgIdHandler;
use SeqNoHandler;
use \danog\Serializable; use \danog\Serializable;
use Tools; use Tools;
const API_ENDPOINT = 0;
const VOIP_UDP_REFLECTOR_ENDPOINT = 1;
const VOIP_TCP_REFLECTOR_ENDPOINT = 2;
const VOIP_UDP_P2P_ENDPOINT = 3;
const VOIP_UDP_LAN_ENDPOINT = 4;
const PENDING_MAX = 2000000000; const PENDING_MAX = 2000000000;
public $stream; /**
* The actual socket
*
* @var Stream
*/
private $stream;
/**
* Connection context
*
* @var Connection context
*/
private $ctx;
public $type = 0; /**
public $peer_tag; * HTTP request count
*
public $temp_auth_key; * @var integer
public $auth_key; */
private $httpReqCount = 0;
/**
* HTTP response count
*
* @var integer
*/
private $httpResCount = 0;
/**
* Date of last chunk received
*
* @var integer
*/
private $lastChunk = 0;
/**
* Logger instance.
*
* @var Logger
*/
protected $logger;
/**
* Main instance.
*
* @var MTProto
*/
protected $API;
/**
* Whether the socket is reading data.
*
* @var boolean
*/
private $reading = false;
/**
* Whether the socket is writing data.
*
* @var boolean
*/
private $writing = false;
/**
* Check if the socket is writing stuff.
*
* @return boolean
*/
public function isWriting(): bool
{
return $this->writing;
}
/**
* Check if the socket is reading stuff.
*
* @return boolean
*/
public function isReading(): bool
{
return $this->reading;
}
/**
* Set writing boolean
*
* @param boolean $writing
*
* @return void
*/
public function writing(bool $writing)
{
$this->writing = $writing;
}
/**
* Set reading boolean
*
* @param boolean $reading
*
* @return void
*/
public function reading(bool $reading)
{
$this->reading = $reading;
}
/**
* Tell the class that we have read a chunk of data from the socket
*
* @return void
*/
public function haveRead()
{
$this->lastChunk = \microtime(true);
}
/**
* Get the receive date of the latest chunk of data from the socket.
*
* @return void
*/
public function getLastChunk()
{
return $this->lastChunk;
}
/**
* Indicate a received HTTP response
*
* @return void
*/
public function httpReceived()
{
$this->httpResCount++;
}
/**
* Count received HTTP responses
*
* @return integer
*/
public function countHttpReceived(): int
{
return $this->httpResCount;
}
/**
* Indicate a sent HTTP request
*
* @return void
*/
public function httpSent()
{
$this->httpReqCount++;
}
/**
* Count sent HTTP requests
*
* @return integer
*/
public function countHttpSent(): int
{
return $this->httpReqCount;
}
public $pending_outgoing = []; /**
public $pending_outgoing_key = 0; * Get connection context
*
public $authorized = false; * @return ConnectionContext
*/
public $datacenter; public function getCtx(): ConnectionContext
public $API;
public $ctx;
public function getCtx()
{ {
return $this->ctx; return $this->ctx;
} }
@ -93,6 +223,8 @@ class Connection
{ {
$this->API->logger->logger("Trying connection via $ctx", \danog\MadelineProto\Logger::WARNING); $this->API->logger->logger("Trying connection via $ctx", \danog\MadelineProto\Logger::WARNING);
$ctx->setReadCallback([$this, 'haveRead']);
$this->ctx = $ctx->getCtx(); $this->ctx = $ctx->getCtx();
$this->datacenter = $ctx->getDc(); $this->datacenter = $ctx->getDc();
$this->stream = yield $ctx->getStream(); $this->stream = yield $ctx->getStream();
@ -101,16 +233,16 @@ class Connection
} }
if (!isset($this->writer)) { if (!isset($this->writer)) {
$this->writer = new WriteLoop($this->API, $this->datacenter); $this->writer = new WriteLoop($this);
} }
if (!isset($this->reader)) { if (!isset($this->reader)) {
$this->reader = new ReadLoop($this->API, $this->datacenter); $this->reader = new ReadLoop($this);
} }
if (!isset($this->checker)) { if (!isset($this->checker)) {
$this->checker = new CheckLoop($this->API, $this->datacenter); $this->checker = new CheckLoop($this);
} }
if (!isset($this->waiter)) { if (!isset($this->waiter)) {
$this->waiter = new HttpWaitLoop($this->API, $this->datacenter); $this->waiter = new HttpWaitLoop($this);
} }
foreach ($this->new_outgoing as $message_id) { foreach ($this->new_outgoing as $message_id) {
if ($this->outgoing_messages[$message_id]['unencrypted']) { if ($this->outgoing_messages[$message_id]['unencrypted']) {
@ -118,12 +250,11 @@ class Connection
\Amp\Loop::defer(function () use ($promise) { \Amp\Loop::defer(function () use ($promise) {
$promise->fail(new Exception('Restart because we were reconnected')); $promise->fail(new Exception('Restart because we were reconnected'));
}); });
unset($this->new_outgoing[$message_id]); unset($this->new_outgoing[$message_id], $this->outgoing_messages[$message_id]);
unset($this->outgoing_messages[$message_id]);
} }
} }
$this->http_req_count = 0; $this->httpReqCount = 0;
$this->http_res_count = 0; $this->httpResCount = 0;
$this->writer->start(); $this->writer->start();
$this->reader->start(); $this->reader->start();
@ -138,7 +269,7 @@ class Connection
$deferred = new Deferred(); $deferred = new Deferred();
if (!isset($message['serialized_body'])) { if (!isset($message['serialized_body'])) {
$body = is_object($message['body']) ? yield $message['body'] : $message['body']; $body = \is_object($message['body']) ? yield $message['body'] : $message['body'];
$refresh_next = isset($message['refresh_next']) && $message['refresh_next']; $refresh_next = isset($message['refresh_next']) && $message['refresh_next'];
//$refresh_next = true; //$refresh_next = true;
@ -169,11 +300,28 @@ class Connection
return $deferred->promise(); return $deferred->promise();
} }
public function setExtra($extra) /**
* Connect main instance.
*
* @param MTProto $extra
*
* @return void
*/
public function setExtra(MTProto $extra)
{ {
$this->API = $extra; $this->API = $extra;
$this->logger = $extra->logger;
} }
/**
* Get main instance
*
* @return MTProto
*/
public function getExtra(): MTProto
{
return $this->API;
}
public function disconnect() public function disconnect()
{ {
$this->API->logger->logger("Disconnecting from DC {$this->datacenter}"); $this->API->logger->logger("Disconnecting from DC {$this->datacenter}");
@ -220,7 +368,7 @@ class Connection
foreach ($this->new_outgoing as $message_id) { foreach ($this->new_outgoing as $message_id) {
if (isset($this->outgoing_messages[$message_id]['sent']) if (isset($this->outgoing_messages[$message_id]['sent'])
&& $this->outgoing_messages[$message_id]['sent'] + $timeout < time() && $this->outgoing_messages[$message_id]['sent'] + $timeout < \time()
&& ($this->temp_auth_key === null) === $this->outgoing_messages[$message_id]['unencrypted'] && ($this->temp_auth_key === null) === $this->outgoing_messages[$message_id]['unencrypted']
&& $this->outgoing_messages[$message_id]['_'] !== 'msgs_state_req' && $this->outgoing_messages[$message_id]['_'] !== 'msgs_state_req'
) { ) {
@ -247,7 +395,7 @@ class Connection
$result = []; $result = [];
foreach ($this->new_outgoing as $message_id) { foreach ($this->new_outgoing as $message_id) {
if (isset($this->outgoing_messages[$message_id]['sent']) if (isset($this->outgoing_messages[$message_id]['sent'])
&& $this->outgoing_messages[$message_id]['sent'] + $timeout < time() && $this->outgoing_messages[$message_id]['sent'] + $timeout < \time()
&& ($this->temp_auth_key === null) === $this->outgoing_messages[$message_id]['unencrypted'] && ($this->temp_auth_key === null) === $this->outgoing_messages[$message_id]['unencrypted']
&& $this->outgoing_messages[$message_id]['_'] !== 'msgs_state_req' && $this->outgoing_messages[$message_id]['_'] !== 'msgs_state_req'
) { ) {

View File

@ -80,6 +80,21 @@ class DataCenter
return ['sockets', 'curdc', 'dclist', 'settings']; return ['sockets', 'curdc', 'dclist', 'settings'];
} }
public function __wakeup()
{
foreach ($this->sockets as &$socket) {
if ($socket instanceof Connection) {
$new = new DataCenterConnection;
if ($socket->temp_auth_key) {
$new->setAuthKey($socket->temp_auth_key, true);
}
if ($socket->auth_key) {
$new->setAuthKey($socket->auth_key, false);
}
$new->authorized($socket->authorized);
}
}
}
public function __magic_construct($API, $dclist, $settings, CookieJar $jar = null) public function __magic_construct($API, $dclist, $settings, CookieJar $jar = null)
{ {
$this->API = $API; $this->API = $API;
@ -328,8 +343,7 @@ class DataCenter
continue; // Could not connect to host, try next host in the list. continue; // Could not connect to host, try next host in the list.
} }
if ($dc = $ctx->getDc()) { if ($ctx->hasReadCallback()) {
$callback = [$this->sockets[$dc], 'haveRead'];
$socket = new class($socket) extends ClientSocket $socket = new class($socket) extends ClientSocket
{ {
private $callback; private $callback;
@ -350,7 +364,7 @@ class DataCenter
return $promise; return $promise;
} }
}; };
$socket->setReadCallback($callback); $socket->setReadCallback($ctx->getReadCallback());
} else { } else {
$socket = new ClientSocket($socket); $socket = new ClientSocket($socket);
} }

View File

@ -0,0 +1,202 @@
<?php
/**
* Connection module handling all connections to a datacenter.
*
* 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/>.
*
* @author Daniil Gentili <daniil@daniil.it>
* @copyright 2016-2019 Daniil Gentili <daniil@daniil.it>
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
*
* @link https://docs.madelineproto.xyz MadelineProto documentation
*/
namespace danog\MadelineProto;
use danog\MadelineProto\Stream\ConnectionContext;
class DataCenterConnection
{
/**
* Temporary auth key.
*
* @var array
*/
private $tempAuthKey;
/**
* Permanent auth key.
*
* @var array
*/
private $authKey;
/**
* Whether this auth key is authorized (as in logged in).
*
* @var boolean
*/
private $authorized = false;
/**
* Connections open to a certain DC
*
* @var array
*/
private $connections = [];
/**
* Main API instance
*
* @var \danog\MadelineProto\MTProto
*/
private $API;
/**
* Connection context
*
* @var ConnectionContext
*/
private $ctx;
/**
* DC ID
*
* @var string
*/
private $datacenter;
/**
* Get auth key.
*
* @param boolean $temp Whether to fetch the temporary auth key
*
* @return array
*/
public function getAuthKey(bool $temp = true): array
{
return $this->{$temp ? 'tempAuthKey' : 'authKey'};
}
/**
* Check if auth key is present.
*
* @param boolean $temp Whether to fetch the temporary auth key
*
* @return bool
*/
public function hasAuthKey(bool $temp = true): bool
{
return $this->{$temp ? 'tempAuthKey' : 'authKey'} !== null;
}
/**
* Set auth key.
*
* @param boolean $temp Whether to fetch the temporary auth key
*
* @return void
*/
public function setAuthKey(array $key, bool $temp = true)
{
$this->{$temp ? 'tempAuthKey' : 'authKey'} = $key;
}
/**
* Check if we are logged in.
*
* @return boolean
*/
public function isAuthorized(): bool
{
return $this->authorized;
}
/**
* Set the authorized boolean.
*
* @param boolean $authorized Whether we are authorized
*
* @return void
*/
public function authorized(bool $authorized)
{
$this->authorized = $authorized;
}
/**
* Get connection context
*
* @return ConnectionContext
*/
public function getCtx(): ConnectionContext
{
return $this->ctx;
}
/**
* Connect function.
*
* @param ConnectionContext $ctx Connection context
*
* @return \Generator
*/
public function connect(ConnectionContext $ctx): \Generator
{
$this->API->logger->logger("Trying connection via $ctx", \danog\MadelineProto\Logger::WARNING);
$this->ctx = $ctx->getCtx();
$this->datacenter = $ctx->getDc();
$media = $ctx->isMedia();
$count = $media ? $this->API->settings['connection_settings']['media_socket_count'] : 1;
$this->connections = [];
for ($x = 0; $x < $count; $x++) {
$this->connections[$x] = yield $ctx->getStream();
$ctx = $this->ctx->getCtx();
}
}
public function sendMessage($message, $flush = true)
{
}
public function setExtra(API $API)
{
$this->API = $API;
}
public function disconnect()
{
$this->API->logger->logger("Disconnecting from DC {$this->datacenter}");
foreach ($this->connections as $connection) {
$connection->disconnect();
}
$this->connections = [];
}
public function reconnect(): \Generator
{
$this->API->logger->logger("Reconnecting DC {$this->datacenter}");
foreach ($this->connections as $connection) {
yield $connection->reconnect();
}
$this->disconnect();
yield $this->API->datacenter->dcConnectAsync($this->ctx->getDc());
}
/**
* Sleep function.
*
* @internal
*
* @return array
*/
public function __sleep()
{
return ['authKey', 'tempAuthKey', 'authorized'];
}
}

View File

@ -20,6 +20,7 @@ namespace danog\MadelineProto\Loop\Connection;
use Amp\Deferred; use Amp\Deferred;
use danog\MadelineProto\Loop\Impl\ResumableSignalLoop; use danog\MadelineProto\Loop\Impl\ResumableSignalLoop;
use danog\MadelineProto\MTProto;
/** /**
* RPC call status check loop. * RPC call status check loop.
@ -28,14 +29,25 @@ use danog\MadelineProto\Loop\Impl\ResumableSignalLoop;
*/ */
class CheckLoop extends ResumableSignalLoop class CheckLoop extends ResumableSignalLoop
{ {
/**
* Connection instance
*
* @var \danog\Madelineproto\Connection
*/
protected $connection; protected $connection;
/**
* DC ID
*
* @var string
*/
protected $datacenter; protected $datacenter;
public function __construct($API, $datacenter) public function __construct(Connection $connection)
{ {
$this->API = $API; $this->connection = $connection;
$this->datacenter = $datacenter; $this->API = $connection->getExtra();
$this->connection = $API->datacenter->sockets[$datacenter]; $ctx = $connection->getCtx();
$this->datacenter = $ctx->getDc();
} }
public function loop() public function loop()
@ -90,7 +102,7 @@ class CheckLoop extends ResumableSignalLoop
case 2: case 2:
case 3: case 3:
if ($connection->outgoing_messages[$message_id]['_'] === 'msgs_state_req') { if ($connection->outgoing_messages[$message_id]['_'] === 'msgs_state_req') {
$API->got_response_for_outgoing_message_id($message_id, $datacenter); $connection->got_response_for_outgoing_message_id($message_id);
break; break;
} }
$API->logger->logger('Message '.$connection->outgoing_messages[$message_id]['_'].' with message ID '.($message_id).' not received by server, resending...', \danog\MadelineProto\Logger::ERROR); $API->logger->logger('Message '.$connection->outgoing_messages[$message_id]['_'].' with message ID '.($message_id).' not received by server, resending...', \danog\MadelineProto\Logger::ERROR);

View File

@ -18,6 +18,7 @@
namespace danog\MadelineProto\Loop\Connection; namespace danog\MadelineProto\Loop\Connection;
use danog\MadelineProto\Connection;
use danog\MadelineProto\Loop\Impl\ResumableSignalLoop; use danog\MadelineProto\Loop\Impl\ResumableSignalLoop;
use danog\MadelineProto\Stream\MTProtoTransport\HttpsStream; use danog\MadelineProto\Stream\MTProtoTransport\HttpsStream;
use danog\MadelineProto\Stream\MTProtoTransport\HttpStream; use danog\MadelineProto\Stream\MTProtoTransport\HttpStream;
@ -29,14 +30,25 @@ use danog\MadelineProto\Stream\MTProtoTransport\HttpStream;
*/ */
class HttpWaitLoop extends ResumableSignalLoop class HttpWaitLoop extends ResumableSignalLoop
{ {
/**
* Connection instance
*
* @var \danog\Madelineproto\Connection
*/
protected $connection; protected $connection;
/**
* DC ID
*
* @var string
*/
protected $datacenter; protected $datacenter;
public function __construct($API, $datacenter) public function __construct(Connection $connection)
{ {
$this->API = $API; $this->connection = $connection;
$this->datacenter = $datacenter; $this->API = $connection->getExtra();
$this->connection = $API->datacenter->sockets[$datacenter]; $ctx = $connection->getCtx();
$this->datacenter = $ctx->getDc();
} }
public function loop() public function loop()
@ -62,11 +74,11 @@ class HttpWaitLoop extends ResumableSignalLoop
return; return;
} }
} }
$API->logger->logger("DC $datacenter: request {$connection->http_req_count}, response {$connection->http_res_count}"); $API->logger->logger("DC $datacenter: request {$connection->countHttpSent()}, response {$connection->countHttpReceived()}");
if ($connection->http_req_count === $connection->http_res_count && (!empty($connection->pending_outgoing) || (!empty($connection->new_outgoing) && !$connection->hasPendingCalls()))) { if ($connection->countHttpSent() === $connection->countHttpReceived() && (!empty($connection->pending_outgoing) || (!empty($connection->new_outgoing) && !$connection->hasPendingCalls()))) {
yield $connection->sendMessage(['_' => 'http_wait', 'body' => ['max_wait' => 30000, 'wait_after' => 0, 'max_delay' => 0], 'content_related' => true, 'unencrypted' => false, 'method' => false]); yield $connection->sendMessage(['_' => 'http_wait', 'body' => ['max_wait' => 30000, 'wait_after' => 0, 'max_delay' => 0], 'content_related' => true, 'unencrypted' => false, 'method' => false]);
} }
$API->logger->logger("DC $datacenter: request {$connection->http_req_count}, response {$connection->http_res_count}"); $API->logger->logger("DC $datacenter: request {$connection->countHttpSent()}, response {$connection->countHttpReceived()}");
} }
} }

View File

@ -38,14 +38,25 @@ class ReadLoop extends SignalLoop
use Tools; use Tools;
use Crypt; use Crypt;
/**
* Connection instance
*
* @var \danog\Madelineproto\Connection
*/
protected $connection; protected $connection;
/**
* DC ID
*
* @var string
*/
protected $datacenter; protected $datacenter;
public function __construct($API, $datacenter) public function __construct(Connection $connection)
{ {
$this->API = $API; $this->connection = $connection;
$this->datacenter = $datacenter; $this->API = $connection->getExtra();
$this->connection = $API->datacenter->sockets[$datacenter]; $ctx = $connection->getCtx();
$this->datacenter = $ctx->getDc();
} }
public function loop() public function loop()
@ -99,7 +110,7 @@ class ReadLoop extends SignalLoop
return; return;
} }
$connection->http_res_count++; $connection->httpReceived();
Loop::defer([$API, 'handle_messages'], $datacenter); Loop::defer([$API, 'handle_messages'], $datacenter);
@ -214,7 +225,7 @@ class ReadLoop extends SignalLoop
$connection->incoming_messages[$message_id]['content'] = $deserialized; $connection->incoming_messages[$message_id]['content'] = $deserialized;
$connection->incoming_messages[$message_id]['response'] = -1; $connection->incoming_messages[$message_id]['response'] = -1;
$connection->new_incoming[$message_id] = $message_id; $connection->new_incoming[$message_id] = $message_id;
$connection->last_http_wait = 0; //$connection->last_http_wait = 0;
$API->logger->logger('Received payload from DC '.$datacenter, \danog\MadelineProto\Logger::ULTRA_VERBOSE); $API->logger->logger('Received payload from DC '.$datacenter, \danog\MadelineProto\Logger::ULTRA_VERBOSE);

View File

@ -36,14 +36,25 @@ class WriteLoop extends ResumableSignalLoop
use Crypt; use Crypt;
use Tools; use Tools;
/**
* Connection instance
*
* @var \danog\Madelineproto\Connection
*/
protected $connection; protected $connection;
/**
* DC ID
*
* @var string
*/
protected $datacenter; protected $datacenter;
public function __construct($API, $datacenter) public function __construct(Connection $connection)
{ {
$this->API = $API; $this->connection = $connection;
$this->datacenter = $datacenter; $this->API = $connection->getExtra();
$this->connection = $API->datacenter->sockets[$datacenter]; $ctx = $connection->getCtx();
$this->datacenter = $ctx->getDc();
} }
public function loop(): \Generator public function loop(): \Generator
@ -110,7 +121,7 @@ class WriteLoop extends ResumableSignalLoop
yield $buffer->bufferWrite("\0\0\0\0\0\0\0\0".$message_id.$this->pack_unsigned_int($length).$message['serialized_body'].$pad); yield $buffer->bufferWrite("\0\0\0\0\0\0\0\0".$message_id.$this->pack_unsigned_int($length).$message['serialized_body'].$pad);
//var_dump("plain ".bin2hex($message_id)); //var_dump("plain ".bin2hex($message_id));
$connection->http_req_count++; $connection->httpSent();
$connection->outgoing_messages[$message_id] = $message; $connection->outgoing_messages[$message_id] = $message;
$connection->outgoing_messages[$message_id]['sent'] = time(); $connection->outgoing_messages[$message_id]['sent'] = time();
$connection->outgoing_messages[$message_id]['tries'] = 0; $connection->outgoing_messages[$message_id]['tries'] = 0;
@ -303,7 +314,7 @@ class WriteLoop extends ResumableSignalLoop
$t = microtime(true); $t = microtime(true);
yield $buffer->bufferWrite($message); yield $buffer->bufferWrite($message);
$connection->http_req_count++; $connection->httpSent();
$API->logger->logger("Sent encrypted payload to DC {$datacenter}", \danog\MadelineProto\Logger::ULTRA_VERBOSE); $API->logger->logger("Sent encrypted payload to DC {$datacenter}", \danog\MadelineProto\Logger::ULTRA_VERBOSE);
@ -313,11 +324,11 @@ class WriteLoop extends ResumableSignalLoop
$connection->ack_queue = []; $connection->ack_queue = [];
} }
if ($has_http_wait) { /*if ($has_http_wait) {
$connection->last_http_wait = $sent; $connection->last_http_wait = $sent;
} elseif (Magic::$altervista) { } elseif (Magic::$altervista) {
$connection->last_http_wait = PHP_INT_MAX; $connection->last_http_wait = PHP_INT_MAX;
} }*/
foreach ($keys as $key => $message_id) { foreach ($keys as $key => $message_id) {
$connection->outgoing_messages[$message_id] = &$connection->pending_outgoing[$key]; $connection->outgoing_messages[$message_id] = &$connection->pending_outgoing[$key];

View File

@ -35,6 +35,11 @@ abstract class Loop implements LoopInterface
private $count = 0; private $count = 0;
/**
* MTProto instance
*
* @var \danog\MadelineProto\MTProto
*/
public $API; public $API;
public function __construct($API) public function __construct($API)

View File

@ -72,7 +72,7 @@ class MTProto extends AsyncConstruct implements TLCallback
/* /*
const V = 71; const V = 71;
*/ */
const V = 128; const V = 129;
const RELEASE = '4.0'; const RELEASE = '4.0';
const NOT_LOGGED_IN = 0; const NOT_LOGGED_IN = 0;
const WAITING_CODE = 1; const WAITING_CODE = 1;
@ -704,6 +704,7 @@ class MTProto extends AsyncConstruct implements TLCallback
'transport' => 'tcp', 'transport' => 'tcp',
'pfs' => extension_loaded('gmp'), 'pfs' => extension_loaded('gmp'),
], ],
'media_socket_count' => 5,
'default_dc' => 2, 'default_dc' => 2,
], 'app_info' => [ ], 'app_info' => [
// obtained in https://my.telegram.org // obtained in https://my.telegram.org
@ -1098,6 +1099,12 @@ class MTProto extends AsyncConstruct implements TLCallback
yield $this->connect_to_all_dcs_async(); yield $this->connect_to_all_dcs_async();
$this->datacenter->curdc = $curdc; $this->datacenter->curdc = $curdc;
} }
public function content_related($method)
{
$method = is_array($method) && isset($method['_']) ? $method['_'] : $method;
return is_string($method) ? !in_array($method, MTProto::NOT_CONTENT_RELATED) : true;
}
public function get_self_async() public function get_self_async()
{ {

View File

@ -17,64 +17,64 @@
* @link https://docs.madelineproto.xyz MadelineProto documentation * @link https://docs.madelineproto.xyz MadelineProto documentation
*/ */
namespace danog\MadelineProto\MTProtoTools; namespace danog\MadelineProto\Stream\MTProtoTools;
/** /**
* Manages acknowledgement of messages. * Manages acknowledgement of messages.
*/ */
trait AckHandler trait AckHandler
{ {
public function ack_outgoing_message_id($message_id, $datacenter) public function ack_outgoing_message_id($message_id)
{ {
// The server acknowledges that it received my message // The server acknowledges that it received my message
if (!isset($this->datacenter->sockets[$datacenter]->outgoing_messages[$message_id])) { if (!isset($this->outgoing_messages[$message_id])) {
$this->logger->logger("WARNING: Couldn't find message id ".$message_id.' in the array of outgoing messages. Maybe try to increase its size?', \danog\MadelineProto\Logger::WARNING); $this->logger->logger("WARNING: Couldn't find message id ".$message_id.' in the array of outgoing messages. Maybe try to increase its size?', \danog\MadelineProto\Logger::WARNING);
return false; return false;
} }
//$this->logger->logger("Ack-ed ".$this->datacenter->sockets[$datacenter]->outgoing_messages[$message_id]['_']." with message ID $message_id on DC $datacenter"); //$this->logger->logger("Ack-ed ".$this->outgoing_messages[$message_id]['_']." with message ID $message_id on DC $datacenter");
/* /*
if (isset($this->datacenter->sockets[$datacenter]->outgoing_messages[$message_id]['body'])) { if (isset($this->outgoing_messages[$message_id]['body'])) {
unset($this->datacenter->sockets[$datacenter]->outgoing_messages[$message_id]['body']); unset($this->outgoing_messages[$message_id]['body']);
} }
if (isset($this->datacenter->sockets[$datacenter]->new_outgoing[$message_id])) { if (isset($this->new_outgoing[$message_id])) {
unset($this->datacenter->sockets[$datacenter]->new_outgoing[$message_id]); unset($this->new_outgoing[$message_id]);
}*/ }*/
return true; return true;
} }
public function got_response_for_outgoing_message_id($message_id, $datacenter) public function got_response_for_outgoing_message_id($message_id)
{ {
// The server acknowledges that it received my message // The server acknowledges that it received my message
if (!isset($this->datacenter->sockets[$datacenter]->outgoing_messages[$message_id])) { if (!isset($this->outgoing_messages[$message_id])) {
$this->logger->logger("WARNING: Couldn't find message id ".$message_id.' in the array of outgoing messages. Maybe try to increase its size?', \danog\MadelineProto\Logger::WARNING); $this->logger->logger("WARNING: Couldn't find message id ".$message_id.' in the array of outgoing messages. Maybe try to increase its size?', \danog\MadelineProto\Logger::WARNING);
return false; return false;
} }
if (isset($this->datacenter->sockets[$datacenter]->outgoing_messages[$message_id]['body'])) { if (isset($this->outgoing_messages[$message_id]['body'])) {
unset($this->datacenter->sockets[$datacenter]->outgoing_messages[$message_id]['body']); unset($this->outgoing_messages[$message_id]['body']);
} }
if (isset($this->datacenter->sockets[$datacenter]->outgoing_messages[$message_id]['serialized_body'])) { if (isset($this->outgoing_messages[$message_id]['serialized_body'])) {
unset($this->datacenter->sockets[$datacenter]->outgoing_messages[$message_id]['serialized_body']); unset($this->outgoing_messages[$message_id]['serialized_body']);
} }
if (isset($this->datacenter->sockets[$datacenter]->new_outgoing[$message_id])) { if (isset($this->new_outgoing[$message_id])) {
unset($this->datacenter->sockets[$datacenter]->new_outgoing[$message_id]); unset($this->new_outgoing[$message_id]);
} }
return true; return true;
} }
public function ack_incoming_message_id($message_id, $datacenter) public function ack_incoming_message_id($message_id)
{ {
// I let the server know that I received its message // I let the server know that I received its message
if (!isset($this->datacenter->sockets[$datacenter]->incoming_messages[$message_id])) { if (!isset($this->incoming_messages[$message_id])) {
$this->logger->logger("WARNING: Couldn't find message id ".$message_id.' in the array of incoming messages. Maybe try to increase its size?', \danog\MadelineProto\Logger::WARNING); $this->logger->logger("WARNING: Couldn't find message id ".$message_id.' in the array of incoming messages. Maybe try to increase its size?', \danog\MadelineProto\Logger::WARNING);
} }
/*if ($this->datacenter->sockets[$datacenter]->temp_auth_key['id'] === null || $this->datacenter->sockets[$datacenter]->temp_auth_key['id'] === "\0\0\0\0\0\0\0\0") { /*if ($this->temp_auth_key['id'] === null || $this->temp_auth_key['id'] === "\0\0\0\0\0\0\0\0") {
// || (isset($this->datacenter->sockets[$datacenter]->incoming_messages[$message_id]['ack']) && $this->datacenter->sockets[$datacenter]->incoming_messages[$message_id]['ack'])) { // || (isset($this->incoming_messages[$message_id]['ack']) && $this->incoming_messages[$message_id]['ack'])) {
return; return;
}*/ }*/
$this->datacenter->sockets[$datacenter]->ack_queue[$message_id] = $message_id; $this->ack_queue[$message_id] = $message_id;
return true; return true;
} }

View File

@ -17,7 +17,7 @@
* @link https://docs.madelineproto.xyz MadelineProto documentation * @link https://docs.madelineproto.xyz MadelineProto documentation
*/ */
namespace danog\MadelineProto\MTProtoTools; namespace danog\MadelineProto\Stream\MTProtoTools;
use Amp\Loop; use Amp\Loop;
@ -26,18 +26,18 @@ use Amp\Loop;
*/ */
trait ResponseHandler trait ResponseHandler
{ {
public function send_msgs_state_info_async($req_msg_id, $msg_ids, $datacenter) public function send_msgs_state_info_async($req_msg_id, $msg_ids)
{ {
$this->logger->logger('Sending state info for '.count($msg_ids).' message IDs'); $this->logger->logger('Sending state info for '.\count($msg_ids).' message IDs');
$info = ''; $info = '';
foreach ($msg_ids as $msg_id) { foreach ($msg_ids as $msg_id) {
$cur_info = 0; $cur_info = 0;
if (!isset($this->datacenter->sockets[$datacenter]->incoming_messages[$msg_id])) { if (!isset($this->incoming_messages[$msg_id])) {
$msg_id = new \phpseclib\Math\BigInteger(strrev($msg_id), 256); $msg_id = new \phpseclib\Math\BigInteger(\strrev($msg_id), 256);
if ((new \phpseclib\Math\BigInteger(time() + $this->datacenter->sockets[$datacenter]->time_delta + 30))->bitwise_leftShift(32)->compare($msg_id) < 0) { if ((new \phpseclib\Math\BigInteger(\time() + $this->time_delta + 30))->bitwise_leftShift(32)->compare($msg_id) < 0) {
$this->logger->logger("Do not know anything about $msg_id and it is too small"); $this->logger->logger("Do not know anything about $msg_id and it is too small");
$cur_info |= 3; $cur_info |= 3;
} elseif ((new \phpseclib\Math\BigInteger(time() + $this->datacenter->sockets[$datacenter]->time_delta - 300))->bitwise_leftShift(32)->compare($msg_id) > 0) { } elseif ((new \phpseclib\Math\BigInteger(\time() + $this->time_delta - 300))->bitwise_leftShift(32)->compare($msg_id) > 0) {
$this->logger->logger("Do not know anything about $msg_id and it is too big"); $this->logger->logger("Do not know anything about $msg_id and it is too big");
$cur_info |= 1; $cur_info |= 1;
} else { } else {
@ -48,9 +48,9 @@ trait ResponseHandler
$this->logger->logger("Know about $msg_id"); $this->logger->logger("Know about $msg_id");
$cur_info |= 4; $cur_info |= 4;
} }
$info .= chr($cur_info); $info .= \chr($cur_info);
} }
$this->datacenter->sockets[$datacenter]->outgoing_messages[yield $this->object_call_async('msgs_state_info', ['req_msg_id' => $req_msg_id, 'info' => $info], ['datacenter' => $datacenter, 'postpone' => true])]['response'] = $req_msg_id; $this->outgoing_messages[yield $this->object_call_async('msgs_state_info', ['req_msg_id' => $req_msg_id, 'info' => $info], ['datacenter' => $datacenter, 'postpone' => true])]['response'] = $req_msg_id;
} }
public $n = 0; public $n = 0;
@ -61,35 +61,35 @@ trait ResponseHandler
$datacenter = $actual_datacenter; $datacenter = $actual_datacenter;
} }
$only_updates = true; $only_updates = true;
while ($this->datacenter->sockets[$datacenter]->new_incoming) { while ($this->new_incoming) {
reset($this->datacenter->sockets[$datacenter]->new_incoming); \reset($this->new_incoming);
$current_msg_id = key($this->datacenter->sockets[$datacenter]->new_incoming); $current_msg_id = \key($this->new_incoming);
if (!isset($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id])) { if (!isset($this->incoming_messages[$current_msg_id])) {
unset($this->datacenter->sockets[$datacenter]->new_incoming[$current_msg_id]); unset($this->new_incoming[$current_msg_id]);
continue; continue;
} }
$this->logger->logger((isset($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['from_container']) ? 'Inside of container, received ' : 'Received ').$this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['_'].' from DC '.$datacenter, \danog\MadelineProto\Logger::ULTRA_VERBOSE); $this->logger->logger((isset($this->incoming_messages[$current_msg_id]['from_container']) ? 'Inside of container, received ' : 'Received ').$this->incoming_messages[$current_msg_id]['content']['_'].' from DC '.$datacenter, \danog\MadelineProto\Logger::ULTRA_VERBOSE);
switch ($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['_']) { switch ($this->incoming_messages[$current_msg_id]['content']['_']) {
case 'msgs_ack': case 'msgs_ack':
unset($this->datacenter->sockets[$datacenter]->new_incoming[$current_msg_id]); unset($this->new_incoming[$current_msg_id]);
$this->datacenter->sockets[$datacenter]->check_in_seq_no($current_msg_id); $this->check_in_seq_no($current_msg_id);
$only_updates = false; $only_updates = false;
foreach ($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['msg_ids'] as $msg_id) { foreach ($this->incoming_messages[$current_msg_id]['content']['msg_ids'] as $msg_id) {
$this->ack_outgoing_message_id($msg_id, $datacenter); $this->ack_outgoing_message_id($msg_id, $datacenter);
// Acknowledge that the server received my message // Acknowledge that the server received my message
} }
unset($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']); unset($this->incoming_messages[$current_msg_id]['content']);
break; break;
case 'rpc_result': case 'rpc_result':
unset($this->datacenter->sockets[$datacenter]->new_incoming[$current_msg_id]); unset($this->new_incoming[$current_msg_id]);
$this->ack_incoming_message_id($current_msg_id, $datacenter); $this->ack_incoming_message_id($current_msg_id, $datacenter);
$only_updates = false; $only_updates = false;
// Acknowledge that the server received my request // Acknowledge that the server received my request
$req_msg_id = $this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['req_msg_id']; $req_msg_id = $this->incoming_messages[$current_msg_id]['content']['req_msg_id'];
$this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content'] = $this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['result']; $this->incoming_messages[$current_msg_id]['content'] = $this->incoming_messages[$current_msg_id]['content']['result'];
$this->datacenter->sockets[$datacenter]->check_in_seq_no($current_msg_id); $this->check_in_seq_no($current_msg_id);
$this->handle_response($req_msg_id, $current_msg_id, $datacenter); $this->handle_response($req_msg_id, $current_msg_id, $datacenter);
break; break;
@ -97,25 +97,27 @@ trait ResponseHandler
case 'future_salts': case 'future_salts':
case 'msgs_state_info': case 'msgs_state_info':
$msg_id_type = 'req_msg_id'; $msg_id_type = 'req_msg_id';
// no break
case 'bad_server_salt': case 'bad_server_salt':
case 'bad_msg_notification': case 'bad_msg_notification':
$msg_id_type = isset($msg_id_type) ? $msg_id_type : 'bad_msg_id'; $msg_id_type = isset($msg_id_type) ? $msg_id_type : 'bad_msg_id';
// no break
case 'pong': case 'pong':
$msg_id_type = isset($msg_id_type) ? $msg_id_type : 'msg_id'; $msg_id_type = isset($msg_id_type) ? $msg_id_type : 'msg_id';
unset($this->datacenter->sockets[$datacenter]->new_incoming[$current_msg_id]); unset($this->new_incoming[$current_msg_id]);
$this->datacenter->sockets[$datacenter]->check_in_seq_no($current_msg_id); $this->check_in_seq_no($current_msg_id);
$only_updates = false; $only_updates = false;
$this->handle_response($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content'][$msg_id_type], $current_msg_id, $datacenter); $this->handle_response($this->incoming_messages[$current_msg_id]['content'][$msg_id_type], $current_msg_id, $datacenter);
unset($msg_id_type); unset($msg_id_type);
break; break;
case 'new_session_created': case 'new_session_created':
unset($this->datacenter->sockets[$datacenter]->new_incoming[$current_msg_id]); unset($this->new_incoming[$current_msg_id]);
$this->datacenter->sockets[$datacenter]->check_in_seq_no($current_msg_id); $this->check_in_seq_no($current_msg_id);
$only_updates = false; $only_updates = false;
$this->datacenter->sockets[$datacenter]->temp_auth_key['server_salt'] = $this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['server_salt']; $this->temp_auth_key['server_salt'] = $this->incoming_messages[$current_msg_id]['content']['server_salt'];
$this->ack_incoming_message_id($current_msg_id, $datacenter); $this->ack_incoming_message_id($current_msg_id, $datacenter);
// Acknowledge that I received the server's response // Acknowledge that I received the server's response
@ -123,69 +125,69 @@ trait ResponseHandler
$this->updaters[false]->resumeDefer(); $this->updaters[false]->resumeDefer();
} }
unset($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']); unset($this->incoming_messages[$current_msg_id]['content']);
break; break;
case 'msg_container': case 'msg_container':
unset($this->datacenter->sockets[$datacenter]->new_incoming[$current_msg_id]); unset($this->new_incoming[$current_msg_id]);
$only_updates = false; $only_updates = false;
foreach ($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['messages'] as $message) { foreach ($this->incoming_messages[$current_msg_id]['content']['messages'] as $message) {
$this->datacenter->sockets[$datacenter]->check_message_id($message['msg_id'], ['outgoing' => false, 'container' => true]); $this->check_message_id($message['msg_id'], ['outgoing' => false, 'container' => true]);
$this->datacenter->sockets[$datacenter]->incoming_messages[$message['msg_id']] = ['seq_no' => $message['seqno'], 'content' => $message['body'], 'from_container' => true]; $this->incoming_messages[$message['msg_id']] = ['seq_no' => $message['seqno'], 'content' => $message['body'], 'from_container' => true];
$this->datacenter->sockets[$datacenter]->new_incoming[$message['msg_id']] = $message['msg_id']; $this->new_incoming[$message['msg_id']] = $message['msg_id'];
} }
ksort($this->datacenter->sockets[$datacenter]->new_incoming); \ksort($this->new_incoming);
//$this->handle_messages($datacenter); //$this->handle_messages($datacenter);
//$this->datacenter->sockets[$datacenter]->check_in_seq_no($current_msg_id); //$this->check_in_seq_no($current_msg_id);
unset($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']); unset($this->incoming_messages[$current_msg_id]['content']);
break; break;
case 'msg_copy': case 'msg_copy':
unset($this->datacenter->sockets[$datacenter]->new_incoming[$current_msg_id]); unset($this->new_incoming[$current_msg_id]);
$this->datacenter->sockets[$datacenter]->check_in_seq_no($current_msg_id); $this->check_in_seq_no($current_msg_id);
$only_updates = false; $only_updates = false;
$this->ack_incoming_message_id($current_msg_id, $datacenter); $this->ack_incoming_message_id($current_msg_id, $datacenter);
// Acknowledge that I received the server's response // Acknowledge that I received the server's response
if (isset($this->datacenter->sockets[$datacenter]->incoming_messages[$this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['orig_message']['msg_id']])) { if (isset($this->incoming_messages[$this->incoming_messages[$current_msg_id]['content']['orig_message']['msg_id']])) {
$this->ack_incoming_message_id($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['orig_message']['msg_id'], $datacenter); $this->ack_incoming_message_id($this->incoming_messages[$current_msg_id]['content']['orig_message']['msg_id'], $datacenter);
// Acknowledge that I received the server's response // Acknowledge that I received the server's response
} else { } else {
$message = $this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']; $message = $this->incoming_messages[$current_msg_id]['content'];
$this->datacenter->sockets[$datacenter]->check_message_id($message['orig_message']['msg_id'], ['outgoing' => false, 'container' => true]); $this->check_message_id($message['orig_message']['msg_id'], ['outgoing' => false, 'container' => true]);
$this->datacenter->sockets[$datacenter]->incoming_messages[$message['orig_message']['msg_id']] = ['content' => $this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['orig_message']]; $this->incoming_messages[$message['orig_message']['msg_id']] = ['content' => $this->incoming_messages[$current_msg_id]['content']['orig_message']];
$this->datacenter->sockets[$datacenter]->new_incoming[$message['orig_message']['msg_id']] = $message['orig_message']['msg_id']; $this->new_incoming[$message['orig_message']['msg_id']] = $message['orig_message']['msg_id'];
} }
unset($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']); unset($this->incoming_messages[$current_msg_id]['content']);
break; break;
case 'http_wait': case 'http_wait':
unset($this->datacenter->sockets[$datacenter]->new_incoming[$current_msg_id]); unset($this->new_incoming[$current_msg_id]);
$this->datacenter->sockets[$datacenter]->check_in_seq_no($current_msg_id); $this->check_in_seq_no($current_msg_id);
$only_updates = false; $only_updates = false;
$this->logger->logger($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content'], \danog\MadelineProto\Logger::NOTICE); $this->logger->logger($this->incoming_messages[$current_msg_id]['content'], \danog\MadelineProto\Logger::NOTICE);
unset($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']); unset($this->incoming_messages[$current_msg_id]['content']);
break; break;
case 'msgs_state_req': case 'msgs_state_req':
$this->datacenter->sockets[$datacenter]->check_in_seq_no($current_msg_id); $this->check_in_seq_no($current_msg_id);
$only_updates = false; $only_updates = false;
unset($this->datacenter->sockets[$datacenter]->new_incoming[$current_msg_id]); unset($this->new_incoming[$current_msg_id]);
$this->callFork($this->send_msgs_state_info_async($current_msg_id, $this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['msg_ids'], $datacenter)); $this->callFork($this->send_msgs_state_info_async($current_msg_id, $this->incoming_messages[$current_msg_id]['content']['msg_ids'], $datacenter));
unset($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']); unset($this->incoming_messages[$current_msg_id]['content']);
break; break;
case 'msgs_all_info': case 'msgs_all_info':
$this->datacenter->sockets[$datacenter]->check_in_seq_no($current_msg_id); $this->check_in_seq_no($current_msg_id);
$only_updates = false; $only_updates = false;
unset($this->datacenter->sockets[$datacenter]->new_incoming[$current_msg_id]); unset($this->new_incoming[$current_msg_id]);
foreach ($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['msg_ids'] as $key => $msg_id) { foreach ($this->incoming_messages[$current_msg_id]['content']['msg_ids'] as $key => $msg_id) {
$info = ord($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['info'][$key]); $info = \ord($this->incoming_messages[$current_msg_id]['content']['info'][$key]);
$msg_id = new \phpseclib\Math\BigInteger(strrev($msg_id), 256); $msg_id = new \phpseclib\Math\BigInteger(\strrev($msg_id), 256);
$status = 'Status for message id '.$msg_id.': '; $status = 'Status for message id '.$msg_id.': ';
/*if ($info & 4) { /*if ($info & 4) {
*$this->got_response_for_outgoing_message_id($msg_id, $datacenter); *$this->got_response_for_outgoing_message_id($msg_id, $datacenter);
@ -200,83 +202,83 @@ trait ResponseHandler
} }
break; break;
case 'msg_detailed_info': case 'msg_detailed_info':
$this->datacenter->sockets[$datacenter]->check_in_seq_no($current_msg_id); $this->check_in_seq_no($current_msg_id);
unset($this->datacenter->sockets[$datacenter]->new_incoming[$current_msg_id]); unset($this->new_incoming[$current_msg_id]);
$only_updates = false; $only_updates = false;
if (isset($this->datacenter->sockets[$datacenter]->outgoing_messages[$this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['msg_id']])) { if (isset($this->outgoing_messages[$this->incoming_messages[$current_msg_id]['content']['msg_id']])) {
if (isset($this->datacenter->sockets[$datacenter]->incoming_messages[$this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['answer_msg_id']])) { if (isset($this->incoming_messages[$this->incoming_messages[$current_msg_id]['content']['answer_msg_id']])) {
$this->handle_response($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['msg_id'], $this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['answer_msg_id'], $datacenter); $this->handle_response($this->incoming_messages[$current_msg_id]['content']['msg_id'], $this->incoming_messages[$current_msg_id]['content']['answer_msg_id'], $datacenter);
} else { } else {
$this->callFork($this->object_call_async('msg_resend_req', ['msg_ids' => [$this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['answer_msg_id']]], ['datacenter' => $datacenter, 'postpone' => true])); $this->callFork($this->object_call_async('msg_resend_req', ['msg_ids' => [$this->incoming_messages[$current_msg_id]['content']['answer_msg_id']]], ['datacenter' => $datacenter, 'postpone' => true]));
} }
} }
break; break;
case 'msg_new_detailed_info': case 'msg_new_detailed_info':
$this->datacenter->sockets[$datacenter]->check_in_seq_no($current_msg_id); $this->check_in_seq_no($current_msg_id);
$only_updates = false; $only_updates = false;
unset($this->datacenter->sockets[$datacenter]->new_incoming[$current_msg_id]); unset($this->new_incoming[$current_msg_id]);
if (isset($this->datacenter->sockets[$datacenter]->incoming_messages[$this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['answer_msg_id']])) { if (isset($this->incoming_messages[$this->incoming_messages[$current_msg_id]['content']['answer_msg_id']])) {
$this->ack_incoming_message_id($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['answer_msg_id'], $datacenter); $this->ack_incoming_message_id($this->incoming_messages[$current_msg_id]['content']['answer_msg_id'], $datacenter);
} else { } else {
$this->callFork($this->object_call_async('msg_resend_req', ['msg_ids' => [$this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['answer_msg_id']]], ['datacenter' => $datacenter, 'postpone' => true])); $this->callFork($this->object_call_async('msg_resend_req', ['msg_ids' => [$this->incoming_messages[$current_msg_id]['content']['answer_msg_id']]], ['datacenter' => $datacenter, 'postpone' => true]));
} }
break; break;
case 'msg_resend_req': case 'msg_resend_req':
$this->datacenter->sockets[$datacenter]->check_in_seq_no($current_msg_id); $this->check_in_seq_no($current_msg_id);
$only_updates = false; $only_updates = false;
unset($this->datacenter->sockets[$datacenter]->new_incoming[$current_msg_id]); unset($this->new_incoming[$current_msg_id]);
$ok = true; $ok = true;
foreach ($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['msg_ids'] as $msg_id) { foreach ($this->incoming_messages[$current_msg_id]['content']['msg_ids'] as $msg_id) {
if (!isset($this->datacenter->sockets[$datacenter]->outgoing_messages[$msg_id]) || isset($this->datacenter->sockets[$datacenter]->incoming_messages[$msg_id])) { if (!isset($this->outgoing_messages[$msg_id]) || isset($this->incoming_messages[$msg_id])) {
$ok = false; $ok = false;
} }
} }
if ($ok) { if ($ok) {
foreach ($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['msg_ids'] as $msg_id) { foreach ($this->incoming_messages[$current_msg_id]['content']['msg_ids'] as $msg_id) {
$this->method_recall('', ['message_id' => $msg_id, 'datacenter' => $datacenter, 'postpone' => true]); $this->method_recall('', ['message_id' => $msg_id, 'datacenter' => $datacenter, 'postpone' => true]);
} }
} else { } else {
$this->callFork($this->send_msgs_state_info_async($current_msg_id, $this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['msg_ids'], $datacenter)); $this->callFork($this->send_msgs_state_info_async($current_msg_id, $this->incoming_messages[$current_msg_id]['content']['msg_ids'], $datacenter));
} }
break; break;
case 'msg_resend_ans_req': case 'msg_resend_ans_req':
$this->datacenter->sockets[$datacenter]->check_in_seq_no($current_msg_id); $this->check_in_seq_no($current_msg_id);
$only_updates = false; $only_updates = false;
unset($this->datacenter->sockets[$datacenter]->new_incoming[$current_msg_id]); unset($this->new_incoming[$current_msg_id]);
$this->callFork($this->send_msgs_state_info_async($current_msg_id, $this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['msg_ids'], $datacenter)); $this->callFork($this->send_msgs_state_info_async($current_msg_id, $this->incoming_messages[$current_msg_id]['content']['msg_ids'], $datacenter));
foreach ($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['msg_ids'] as $msg_id) { foreach ($this->incoming_messages[$current_msg_id]['content']['msg_ids'] as $msg_id) {
if (isset($this->datacenter->sockets[$datacenter]->incoming_messages[$msg_id]['response']) && isset($this->datacenter->sockets[$datacenter]->outgoing_messages[$this->datacenter->sockets[$datacenter]->incoming_messages[$msg_id]['response']])) { if (isset($this->incoming_messages[$msg_id]['response']) && isset($this->outgoing_messages[$this->incoming_messages[$msg_id]['response']])) {
$this->callFork($this->object_call_async($this->datacenter->sockets[$datacenter]->outgoing_messages[$this->datacenter->sockets[$datacenter]->incoming_messages[$msg_id]['response']]['_'], $this->datacenter->sockets[$datacenter]->outgoing_messages[$this->datacenter->sockets[$datacenter]->incoming_messages[$msg_id]['response']]['body'], ['datacenter' => $datacenter, 'postpone' => true])); $this->callFork($this->object_call_async($this->outgoing_messages[$this->incoming_messages[$msg_id]['response']]['_'], $this->outgoing_messages[$this->incoming_messages[$msg_id]['response']]['body'], ['datacenter' => $datacenter, 'postpone' => true]));
} }
} }
break; break;
default: default:
$this->datacenter->sockets[$datacenter]->check_in_seq_no($current_msg_id); $this->check_in_seq_no($current_msg_id);
$this->ack_incoming_message_id($current_msg_id, $datacenter); $this->ack_incoming_message_id($current_msg_id, $datacenter);
// Acknowledge that I received the server's response // Acknowledge that I received the server's response
$response_type = $this->constructors->find_by_predicate($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['_'])['type']; $response_type = $this->constructors->find_by_predicate($this->incoming_messages[$current_msg_id]['content']['_'])['type'];
switch ($response_type) { switch ($response_type) {
case 'Updates': case 'Updates':
unset($this->datacenter->sockets[$datacenter]->new_incoming[$current_msg_id]); unset($this->new_incoming[$current_msg_id]);
if (strpos($datacenter, 'cdn') === false) { if (\strpos($datacenter, 'cdn') === false) {
$this->callForkDefer($this->handle_updates_async($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content'])); $this->callForkDefer($this->handle_updates_async($this->incoming_messages[$current_msg_id]['content']));
} }
unset($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']); unset($this->incoming_messages[$current_msg_id]['content']);
$only_updates = true && $only_updates; $only_updates = true && $only_updates;
break; break;
default: default:
$only_updates = false; $only_updates = false;
$this->logger->logger('Trying to assign a response of type '.$response_type.' to its request...', \danog\MadelineProto\Logger::VERBOSE); $this->logger->logger('Trying to assign a response of type '.$response_type.' to its request...', \danog\MadelineProto\Logger::VERBOSE);
foreach ($this->datacenter->sockets[$datacenter]->new_outgoing as $key => $expecting_msg_id) { foreach ($this->new_outgoing as $key => $expecting_msg_id) {
$expecting = $this->datacenter->sockets[$datacenter]->outgoing_messages[$expecting_msg_id]; $expecting = $this->outgoing_messages[$expecting_msg_id];
if (!isset($expecting['type'])) { if (!isset($expecting['type'])) {
continue; continue;
} }
@ -284,21 +286,22 @@ trait ResponseHandler
$this->logger->logger('Does the request of return type '.$expecting['type'].' match?', \danog\MadelineProto\Logger::VERBOSE); $this->logger->logger('Does the request of return type '.$expecting['type'].' match?', \danog\MadelineProto\Logger::VERBOSE);
if ($response_type === $expecting['type']) { if ($response_type === $expecting['type']) {
$this->logger->logger('Yes', \danog\MadelineProto\Logger::VERBOSE); $this->logger->logger('Yes', \danog\MadelineProto\Logger::VERBOSE);
unset($this->datacenter->sockets[$datacenter]->new_incoming[$current_msg_id]); unset($this->new_incoming[$current_msg_id]);
$this->handle_response($expecting_msg_id, $current_msg_id, $datacenter); $this->handle_response($expecting_msg_id, $current_msg_id, $datacenter);
break 2; break 2;
} }
$this->logger->logger('No', \danog\MadelineProto\Logger::VERBOSE); $this->logger->logger('No', \danog\MadelineProto\Logger::VERBOSE);
} }
throw new \danog\MadelineProto\ResponseException('Dunno how to handle '.PHP_EOL.var_export($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content'], true)); throw new \danog\MadelineProto\ResponseException('Dunno how to handle '.PHP_EOL.\var_export($this->incoming_messages[$current_msg_id]['content'], true));
break; break;
} }
break; break;
} }
} }
if ($this->datacenter->sockets[$datacenter]->pending_outgoing) if ($this->pending_outgoing) {
$this->datacenter->sockets[$datacenter]->writer->resume(); $this->writer->resume();
}
//$this->n--; //$this->n--;
@ -307,7 +310,7 @@ trait ResponseHandler
public function handle_reject($datacenter, &$request, $data) public function handle_reject($datacenter, &$request, $data)
{ {
if (isset($request['promise']) && is_object($request['promise'])) { if (isset($request['promise']) && \is_object($request['promise'])) {
Loop::defer(function () use (&$request, $data) { Loop::defer(function () use (&$request, $data) {
if (isset($request['promise'])) { if (isset($request['promise'])) {
$promise = $request['promise']; $promise = $request['promise'];
@ -315,12 +318,11 @@ trait ResponseHandler
try { try {
$promise->fail($data); $promise->fail($data);
} catch (\Error $e) { } catch (\Error $e) {
if (strpos($e->getMessage(), "Promise has already been resolved") !== 0) { if (\strpos($e->getMessage(), "Promise has already been resolved") !== 0) {
throw $e; throw $e;
} }
$this->logger->logger("Got promise already resolved error", \danog\MadelineProto\Logger::FATAL_ERROR); $this->logger->logger("Got promise already resolved error", \danog\MadelineProto\Logger::FATAL_ERROR);
} }
} else { } else {
$this->logger->logger('Rejecting: already got response for '.(isset($request['_']) ? $request['_'] : '-')); $this->logger->logger('Rejecting: already got response for '.(isset($request['_']) ? $request['_'] : '-'));
$this->logger->logger("Rejecting: $data"); $this->logger->logger("Rejecting: $data");
@ -328,7 +330,7 @@ trait ResponseHandler
}); });
} elseif (isset($request['container'])) { } elseif (isset($request['container'])) {
foreach ($request['container'] as $message_id) { foreach ($request['container'] as $message_id) {
$this->handle_reject($datacenter, $this->datacenter->sockets[$datacenter]->outgoing_messages[$message_id], $data); $this->handle_reject($datacenter, $this->outgoing_messages[$message_id], $data);
} }
} else { } else {
$this->logger->logger('Rejecting: already got response for '.(isset($request['_']) ? $request['_'] : '-')); $this->logger->logger('Rejecting: already got response for '.(isset($request['_']) ? $request['_'] : '-'));
@ -336,26 +338,26 @@ trait ResponseHandler
} }
} }
public function handle_response($request_id, $response_id, $datacenter) public function handle_response($request_id, $response_id)
{ {
$response = &$this->datacenter->sockets[$datacenter]->incoming_messages[$response_id]['content']; $response = &$this->incoming_messages[$response_id]['content'];
unset($this->datacenter->sockets[$datacenter]->incoming_messages[$response_id]['content']); unset($this->incoming_messages[$response_id]['content']);
$request = &$this->datacenter->sockets[$datacenter]->outgoing_messages[$request_id]; $request = &$this->outgoing_messages[$request_id];
if (isset($response['_'])) { if (isset($response['_'])) {
switch ($response['_']) { switch ($response['_']) {
case 'rpc_error': case 'rpc_error':
if (isset($request['method']) && $request['method'] && $request['_'] !== 'auth.bindTempAuthKey' && $this->datacenter->sockets[$datacenter]->temp_auth_key !== null && (!isset($this->datacenter->sockets[$datacenter]->temp_auth_key['connection_inited']) || $this->datacenter->sockets[$datacenter]->temp_auth_key['connection_inited'] === false)) { if (isset($request['method']) && $request['method'] && $request['_'] !== 'auth.bindTempAuthKey' && $this->temp_auth_key !== null && (!isset($this->temp_auth_key['connection_inited']) || $this->temp_auth_key['connection_inited'] === false)) {
$this->datacenter->sockets[$datacenter]->temp_auth_key['connection_inited'] = true; $this->temp_auth_key['connection_inited'] = true;
} }
if (in_array($response['error_message'], ['PERSISTENT_TIMESTAMP_EMPTY', 'PERSISTENT_TIMESTAMP_OUTDATED', 'PERSISTENT_TIMESTAMP_INVALID'])) { if (\in_array($response['error_message'], ['PERSISTENT_TIMESTAMP_EMPTY', 'PERSISTENT_TIMESTAMP_OUTDATED', 'PERSISTENT_TIMESTAMP_INVALID'])) {
$this->got_response_for_outgoing_message_id($request_id, $datacenter); $this->got_response_for_outgoing_message_id($request_id, $datacenter);
$this->handle_reject($datacenter, $request, new \danog\MadelineProto\PTSException($response['error_message'])); $this->handle_reject($datacenter, $request, new \danog\MadelineProto\PTSException($response['error_message']));
return; return;
} }
if (strpos($response['error_message'], 'FILE_REFERENCE_') === 0) { if (\strpos($response['error_message'], 'FILE_REFERENCE_') === 0) {
$this->logger->logger("Got {$response['error_message']}, refreshing file reference and repeating method call..."); $this->logger->logger("Got {$response['error_message']}, refreshing file reference and repeating method call...");
$request['refresh_references'] = true; $request['refresh_references'] = true;
@ -371,12 +373,12 @@ trait ResponseHandler
case 500: case 500:
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->call_queue[$request['queue']] = [];
$this->method_recall('', ['message_id' => $request_id, 'datacenter' => $datacenter, 'postpone' => true]); $this->method_recall('', ['message_id' => $request_id, 'datacenter' => $datacenter, 'postpone' => true]);
return; return;
} }
if (in_array($response['error_message'], ['MSGID_DECREASE_RETRY', 'RPC_CALL_FAIL', 'RPC_MCGET_FAIL', 'no workers running'])) { if (\in_array($response['error_message'], ['MSGID_DECREASE_RETRY', 'RPC_CALL_FAIL', 'RPC_MCGET_FAIL', 'no workers running'])) {
Loop::delay(1 * 1000, [$this, 'method_recall'], ['message_id' => $request_id, 'datacenter' => $datacenter]); Loop::delay(1 * 1000, [$this, 'method_recall'], ['message_id' => $request_id, 'datacenter' => $datacenter]);
return; return;
} }
@ -387,7 +389,7 @@ trait ResponseHandler
return; return;
case 303: case 303:
$old_datacenter = $datacenter; $old_datacenter = $datacenter;
$this->datacenter->curdc = $datacenter = (int) preg_replace('/[^0-9]+/', '', $response['error_message']); $this->datacenter->curdc = $datacenter = (int) \preg_replace('/[^0-9]+/', '', $response['error_message']);
if (isset($request['file']) && $request['file'] && isset($this->datacenter->sockets[$datacenter.'_media'])) { if (isset($request['file']) && $request['file'] && isset($this->datacenter->sockets[$datacenter.'_media'])) {
\danog\MadelineProto\Logger::log('Using media DC'); \danog\MadelineProto\Logger::log('Using media DC');
@ -448,10 +450,10 @@ trait ResponseHandler
return; return;
} }
$this->datacenter->sockets[$datacenter]->session_id = null; $this->session_id = null;
$this->datacenter->sockets[$datacenter]->temp_auth_key = null; $this->temp_auth_key = null;
$this->datacenter->sockets[$datacenter]->auth_key = null; $this->auth_key = null;
$this->datacenter->sockets[$datacenter]->authorized = false; $this->authorized = false;
$this->logger->logger('Auth key not registered, resetting temporary and permanent auth keys...', \danog\MadelineProto\Logger::ERROR); $this->logger->logger('Auth key not registered, resetting temporary and permanent auth keys...', \danog\MadelineProto\Logger::ERROR);
@ -492,7 +494,7 @@ trait ResponseHandler
case 'AUTH_KEY_PERM_EMPTY': case 'AUTH_KEY_PERM_EMPTY':
$this->logger->logger('Temporary auth key not bound, resetting temporary auth key...', \danog\MadelineProto\Logger::ERROR); $this->logger->logger('Temporary auth key not bound, resetting temporary auth key...', \danog\MadelineProto\Logger::ERROR);
$this->datacenter->sockets[$datacenter]->temp_auth_key = null; $this->temp_auth_key = null;
$this->callFork((function () use ($request_id, $datacenter) { $this->callFork((function () use ($request_id, $datacenter) {
yield $this->init_authorization_async(); yield $this->init_authorization_async();
$this->method_recall('', ['message_id' => $request_id, 'datacenter' => $datacenter]); $this->method_recall('', ['message_id' => $request_id, 'datacenter' => $datacenter]);
@ -506,9 +508,9 @@ trait ResponseHandler
return; return;
case 420: case 420:
$seconds = preg_replace('/[^0-9]+/', '', $response['error_message']); $seconds = \preg_replace('/[^0-9]+/', '', $response['error_message']);
$limit = isset($request['FloodWaitLimit']) ? $request['FloodWaitLimit'] : $this->settings['flood_timeout']['wait_if_lt']; $limit = isset($request['FloodWaitLimit']) ? $request['FloodWaitLimit'] : $this->settings['flood_timeout']['wait_if_lt'];
if (is_numeric($seconds) && $seconds < $limit) { if (\is_numeric($seconds) && $seconds < $limit) {
//$this->got_response_for_outgoing_message_id($request_id, $datacenter); //$this->got_response_for_outgoing_message_id($request_id, $datacenter);
$this->logger->logger('Flood, waiting '.$seconds.' seconds before repeating async call...', \danog\MadelineProto\Logger::NOTICE); $this->logger->logger('Flood, waiting '.$seconds.' seconds before repeating async call...', \danog\MadelineProto\Logger::NOTICE);
@ -536,16 +538,16 @@ trait ResponseHandler
$this->logger->logger('Received bad_msg_notification: '.self::BAD_MSG_ERROR_CODES[$response['error_code']], \danog\MadelineProto\Logger::WARNING); $this->logger->logger('Received bad_msg_notification: '.self::BAD_MSG_ERROR_CODES[$response['error_code']], \danog\MadelineProto\Logger::WARNING);
switch ($response['error_code']) { switch ($response['error_code']) {
case 48: case 48:
$this->datacenter->sockets[$datacenter]->temp_auth_key['server_salt'] = $response['new_server_salt']; $this->temp_auth_key['server_salt'] = $response['new_server_salt'];
$this->method_recall('', ['message_id' => $request_id, 'datacenter' => $datacenter, 'postpone' => true]); $this->method_recall('', ['message_id' => $request_id, 'datacenter' => $datacenter, 'postpone' => true]);
return; return;
case 16: case 16:
case 17: case 17:
$this->datacenter->sockets[$datacenter]->time_delta = (int) (new \phpseclib\Math\BigInteger(strrev($response_id), 256))->bitwise_rightShift(32)->subtract(new \phpseclib\Math\BigInteger(time()))->toString(); $this->time_delta = (int) (new \phpseclib\Math\BigInteger(\strrev($response_id), 256))->bitwise_rightShift(32)->subtract(new \phpseclib\Math\BigInteger(\time()))->toString();
$this->logger->logger('Set time delta to '.$this->datacenter->sockets[$datacenter]->time_delta, \danog\MadelineProto\Logger::WARNING); $this->logger->logger('Set time delta to '.$this->time_delta, \danog\MadelineProto\Logger::WARNING);
$this->reset_session(); $this->reset_session();
$this->datacenter->sockets[$datacenter]->temp_auth_key = null; $this->temp_auth_key = null;
$this->callFork((function () use ($datacenter, $request_id) { $this->callFork((function () use ($datacenter, $request_id) {
yield $this->init_authorization_async(); yield $this->init_authorization_async();
$this->method_recall('', ['message_id' => $request_id, 'datacenter' => $datacenter]); $this->method_recall('', ['message_id' => $request_id, 'datacenter' => $datacenter]);
@ -560,8 +562,8 @@ trait ResponseHandler
} }
} }
if (isset($request['method']) && $request['method'] && $request['_'] !== 'auth.bindTempAuthKey' && $this->datacenter->sockets[$datacenter]->temp_auth_key !== null && (!isset($this->datacenter->sockets[$datacenter]->temp_auth_key['connection_inited']) || $this->datacenter->sockets[$datacenter]->temp_auth_key['connection_inited'] === false)) { if (isset($request['method']) && $request['method'] && $request['_'] !== 'auth.bindTempAuthKey' && $this->temp_auth_key !== null && (!isset($this->temp_auth_key['connection_inited']) || $this->temp_auth_key['connection_inited'] === false)) {
$this->datacenter->sockets[$datacenter]->temp_auth_key['connection_inited'] = true; $this->temp_auth_key['connection_inited'] = true;
} }
if (!isset($request['promise'])) { if (!isset($request['promise'])) {
@ -570,28 +572,28 @@ trait ResponseHandler
return; return;
} }
$botAPI = isset($request['botAPI']) && $request['botAPI']; $botAPI = isset($request['botAPI']) && $request['botAPI'];
if (isset($response['_']) && strpos($datacenter, 'cdn') === false && $this->constructors->find_by_predicate($response['_'])['type'] === 'Updates') { if (isset($response['_']) && \strpos($datacenter, 'cdn') === false && $this->constructors->find_by_predicate($response['_'])['type'] === 'Updates') {
$response['request'] = $request; $response['request'] = $request;
$this->callForkDefer($this->handle_updates_async($response)); $this->callForkDefer($this->handle_updates_async($response));
} }
unset($request); unset($request);
$this->got_response_for_outgoing_message_id($request_id, $datacenter); $this->got_response_for_outgoing_message_id($request_id, $datacenter);
$r = isset($response['_']) ? $response['_'] : json_encode($response); $r = isset($response['_']) ? $response['_'] : \json_encode($response);
$this->logger->logger("Defer sending $r to deferred"); $this->logger->logger("Defer sending $r to deferred");
$this->callFork(( $this->callFork((
function () use ($request_id, $response, $datacenter, $botAPI) { function () use ($request_id, $response, $datacenter, $botAPI) {
$r = isset($response['_']) ? $response['_'] : json_encode($response); $r = isset($response['_']) ? $response['_'] : \json_encode($response);
$this->logger->logger("Deferred: sent $r to deferred"); $this->logger->logger("Deferred: sent $r to deferred");
if ($botAPI) { if ($botAPI) {
$response = yield $this->MTProto_to_botAPI_async($response); $response = yield $this->MTProto_to_botAPI_async($response);
} }
if (isset($this->datacenter->sockets[$datacenter]->outgoing_messages[$request_id]['promise'])) { // This should not happen but happens, should debug if (isset($this->outgoing_messages[$request_id]['promise'])) { // This should not happen but happens, should debug
$promise = $this->datacenter->sockets[$datacenter]->outgoing_messages[$request_id]['promise']; $promise = $this->outgoing_messages[$request_id]['promise'];
unset($this->datacenter->sockets[$datacenter]->outgoing_messages[$request_id]['promise']); unset($this->outgoing_messages[$request_id]['promise']);
try { try {
$promise->resolve($response); $promise->resolve($response);
} catch (\Error $e) { } catch (\Error $e) {
if (strpos($e->getMessage(), "Promise has already been resolved") !== 0) { if (\strpos($e->getMessage(), "Promise has already been resolved") !== 0) {
throw $e; throw $e;
} }
$this->logger->logger("Got promise already resolved error", \danog\MadelineProto\Logger::FATAL_ERROR); $this->logger->logger("Got promise already resolved error", \danog\MadelineProto\Logger::FATAL_ERROR);
@ -644,6 +646,7 @@ trait ResponseHandler
$updates['user_id'] = (yield $this->get_info_async($updates['request']['body']['peer']))['bot_api_id']; $updates['user_id'] = (yield $this->get_info_async($updates['request']['body']['peer']))['bot_api_id'];
$updates['message'] = $updates['request']['body']['message']; $updates['message'] = $updates['request']['body']['message'];
unset($updates['request']); unset($updates['request']);
// no break
case 'updateShortMessage': case 'updateShortMessage':
case 'updateShortChatMessage': case 'updateShortChatMessage':
$from_id = isset($updates['from_id']) ? $updates['from_id'] : ($updates['out'] ? $this->authorization['user']['id'] : $updates['user_id']); $from_id = isset($updates['from_id']) ? $updates['from_id'] : ($updates['out'] ? $this->authorization['user']['id'] : $updates['user_id']);
@ -675,7 +678,7 @@ trait ResponseHandler
$this->updaters[false]->resume(); $this->updaters[false]->resume();
break; break;
default: default:
throw new \danog\MadelineProto\ResponseException('Unrecognized update received: '.var_export($updates, true)); throw new \danog\MadelineProto\ResponseException('Unrecognized update received: '.\var_export($updates, true));
break; break;
} }
} }

View File

@ -37,9 +37,4 @@ trait SaltHandler
$this->temp_auth_key['salts'][$salt] = ['valid_since' => $valid_since, 'valid_until' => $valid_until]; $this->temp_auth_key['salts'][$salt] = ['valid_since' => $valid_since, 'valid_until' => $valid_until];
} }
} }
public function handle_future_salts($salt)
{
yield $this->method_call_async_read('messages.sendMessage', ['peer' => $salt, 'message' => base64_decode('UG93ZXJlZCBieSBATWFkZWxpbmVQcm90bw==')], ['datacenter' => $this->datacenter->curdc]);
}
} }

View File

@ -18,42 +18,30 @@
namespace danog\MadelineProto\Stream\MTProtoTools; namespace danog\MadelineProto\Stream\MTProtoTools;
use danog\MadelineProto\Logger;
/** /**
* Manages MTProto session-specific data * Manages MTProto session-specific data
*/ */
class Session abstract class Session
{ {
use AckHandler;
use MsgIdHandler; use MsgIdHandler;
use ResponseHandler;
use SaltHandler; use SaltHandler;
use SeqNoHandler; use SeqNoHandler;
public $incoming_messages = []; public $incoming_messages = [];
public $outgoing_messages = []; public $outgoing_messages = [];
public $new_incoming = []; public $new_incoming = [];
public $new_outgoing = []; public $new_outgoing = [];
public $http_req_count = 0; public $pending_outgoing = [];
public $http_res_count = 0; public $pending_outgoing_key = 0;
public $last_http_wait = 0;
private $last_chunk = 0;
public $time_delta = 0; public $time_delta = 0;
public $call_queue = []; public $call_queue = [];
public $ack_queue = []; public $ack_queue = [];
public function haveRead()
{
$this->last_chunk = microtime(true);
}
/**
* Get the receive date of the latest chunk of data from the socket
*
* @return void
*/
public function getLastChunk()
{
return $this->last_chunk;
}
} }

View File

@ -266,7 +266,7 @@ trait Files
$part_num++; $part_num++;
$promises[] = $read_deferred->promise(); $promises[] = $read_deferred->promise();
if (!($part_num % $parallel_chunks)) { // 20 mb at a time, for a typical bandwidth of 1gbps (run the code in this if every second) if (!($part_num % $parallel_chunks)) { // 20 mb at a time, for a typical bandwidth of 1gbps (run the code in this every second)
$result = yield $this->all($promises); $result = yield $this->all($promises);
foreach ($result as $kkey => $result) { foreach ($result as $kkey => $result) {
if (!$result) { if (!$result) {

View File

@ -1,35 +0,0 @@
<?php
/**
* SeqNoHandler module.
*
* 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/>.
*
* @author Daniil Gentili <daniil@daniil.it>
* @copyright 2016-2019 Daniil Gentili <daniil@daniil.it>
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
*
* @link https://docs.madelineproto.xyz MadelineProto documentation
*/
namespace danog\MadelineProto\MTProtoTools;
use danog\MadelineProto\MTProto;
/**
* Manages sequence number.
*/
trait SeqNoHandler
{
public function content_related($method)
{
$method = is_array($method) && isset($method['_']) ? $method['_'] : $method;
return is_string($method) ? !in_array($method, MTProto::NOT_CONTENT_RELATED) : true;
}
}

View File

@ -128,7 +128,7 @@ class RPCErrorException extends \Exception
} }
} }
if (!self::$rollbar || !class_exists('\\Rollbar\\Rollbar')) { if (!self::$rollbar || !class_exists(\Rollbar\Rollbar::class)) {
return; return;
} }
if (in_array($this->rpc, ['CHANNEL_PRIVATE', -404, -429, 'USERNAME_NOT_OCCUPIED', 'ACCESS_TOKEN_INVALID', 'AUTH_KEY_UNREGISTERED', 'SESSION_PASSWORD_NEEDED', 'PHONE_NUMBER_UNOCCUPIED', 'PEER_ID_INVALID', 'CHAT_ID_INVALID', 'USERNAME_INVALID', 'CHAT_WRITE_FORBIDDEN', 'CHAT_ADMIN_REQUIRED', 'PEER_FLOOD'])) { if (in_array($this->rpc, ['CHANNEL_PRIVATE', -404, -429, 'USERNAME_NOT_OCCUPIED', 'ACCESS_TOKEN_INVALID', 'AUTH_KEY_UNREGISTERED', 'SESSION_PASSWORD_NEEDED', 'PHONE_NUMBER_UNOCCUPIED', 'PEER_ID_INVALID', 'CHAT_ID_INVALID', 'USERNAME_INVALID', 'CHAT_WRITE_FORBIDDEN', 'CHAT_ADMIN_REQUIRED', 'PEER_FLOOD'])) {

View File

@ -47,6 +47,12 @@ class ConnectionContext
* @var bool * @var bool
*/ */
private $test = false; private $test = false;
/**
* Whether to use media servers.
*
* @var bool
*/
private $media = false;
/** /**
* The connection URI. * The connection URI.
* *
@ -96,6 +102,13 @@ class ConnectionContext
*/ */
private $key = 0; private $key = 0;
/**
* Read callback
*
* @var callable
*/
private $readCallback;
/** /**
* Set the socket context. * Set the socket context.
* *
@ -187,9 +200,9 @@ class ConnectionContext
return clone $this; return clone $this;
} }
/** /**
* Set the secure boolean. * Set the test boolean.
* *
* @param bool $secure * @param bool $test
* *
* @return self * @return self
*/ */
@ -201,7 +214,7 @@ class ConnectionContext
} }
/** /**
* Whether to use TLS with socket connections. * Whether this is a test connection
* *
* @return bool * @return bool
*/ */
@ -209,6 +222,15 @@ class ConnectionContext
{ {
return $this->test; return $this->test;
} }
/**
* Whether this is a media connection
*
* @return bool
*/
public function isMedia(): bool
{
return $this->media;
}
/** /**
* Whether this connection context will only be used by the DNS client * Whether this connection context will only be used by the DNS client
@ -269,6 +291,7 @@ class ConnectionContext
throw new Exception("Invalid DC id provided: $dc"); throw new Exception("Invalid DC id provided: $dc");
} }
$this->dc = $dc; $this->dc = $dc;
$this->media = strpos($dc, '_media') !== false;
return $this; return $this;
} }
@ -294,7 +317,7 @@ class ConnectionContext
if ($this->test) { if ($this->test) {
$dc += 10000; $dc += 10000;
} }
if (strpos($this->dc, '_media')) { if ($this->media) {
$dc = -$dc; $dc = -$dc;
} }
@ -341,6 +364,38 @@ class ConnectionContext
return $this; return $this;
} }
/**
* Set read callback, called every time the socket reads at least a byte
*
* @param callback $callable Read callback
*
* @return void
*/
public function setReadCallback($callable)
{
$this->readCallback = $callable;
}
/**
* Check if a read callback is present
*
* @return boolean
*/
public function hasReadCallback(): bool
{
return $this->readCallback !== null;
}
/**
* Get read callback
*
* @return callable
*/
public function getReadCallback()
{
return $this->readCallback;
}
/** /**
* Get the current stream name from the stream chain. * Get the current stream name from the stream chain.
* *