From 46f7e63734cc1248c38d52eb24c3e9a027845cf7 Mon Sep 17 00:00:00 2001 From: Alexander Pankratov Date: Sun, 3 May 2020 04:33:54 +0300 Subject: [PATCH] Async refactor --- src/danog/MadelineProto/Db/DbArray.php | 11 +- .../MadelineProto/Db/DbPropertiesFabric.php | 15 +- src/danog/MadelineProto/Db/DbType.php | 2 +- src/danog/MadelineProto/Db/MemoryArray.php | 28 ++- src/danog/MadelineProto/Db/Mysql.php | 18 +- src/danog/MadelineProto/Db/MysqlArray.php | 168 ++++-------------- src/danog/MadelineProto/InternalDoc.php | 13 +- src/danog/MadelineProto/MTProto.php | 52 +++--- .../MTProtoTools/PeerHandler.php | 100 ++++++----- .../MTProtoTools/ReferenceDatabase.php | 8 +- .../MTProtoTools/UpdateHandler.php | 2 +- .../MadelineProto/Wrappers/DialogHandler.php | 6 +- src/danog/MadelineProto/Wrappers/Login.php | 2 +- 13 files changed, 188 insertions(+), 237 deletions(-) diff --git a/src/danog/MadelineProto/Db/DbArray.php b/src/danog/MadelineProto/Db/DbArray.php index 27b7e538..edfaf2b3 100644 --- a/src/danog/MadelineProto/Db/DbArray.php +++ b/src/danog/MadelineProto/Db/DbArray.php @@ -5,10 +5,13 @@ namespace danog\MadelineProto\Db; use Amp\Producer; use Amp\Promise; -interface DbArray extends DbType, \ArrayAccess, \Countable, \Iterator, \SeekableIterator +interface DbArray extends DbType, \ArrayAccess, \Countable { - public function getArrayCopy(); - public function offsetGetAsync(string $offset): Promise; - public function offsetSetAsync(string $offset, $value): Promise; + public function getArrayCopy(): array; + public function offsetExists($offset): Promise; + public function offsetGet($offset): Promise; + public function offsetSet($offset, $value); + public function offsetUnset($offset): Promise; + public function count(): Promise; public function getIterator(): Producer; } \ No newline at end of file diff --git a/src/danog/MadelineProto/Db/DbPropertiesFabric.php b/src/danog/MadelineProto/Db/DbPropertiesFabric.php index 6739ce3e..cc2dce8c 100644 --- a/src/danog/MadelineProto/Db/DbPropertiesFabric.php +++ b/src/danog/MadelineProto/Db/DbPropertiesFabric.php @@ -2,7 +2,6 @@ namespace danog\MadelineProto\Db; -use danog\MadelineProto\API; use danog\MadelineProto\MTProto; class DbPropertiesFabric @@ -13,7 +12,7 @@ class DbPropertiesFabric * @param string $name * @param $value * - * @return mixed + * @return DbType * * @uses \danog\MadelineProto\Db\MemoryArray * @uses \danog\MadelineProto\Db\SharedMemoryArray @@ -44,8 +43,18 @@ class DbPropertiesFabric throw new \InvalidArgumentException("Unknown $propertyType: {$propertyType}"); } - $prefix = (string) ($madelineProto->getSelf()['id'] ?? 'tmp'); + $prefix = static::getSessionId($madelineProto); return $class::getInstance($name, $value, $prefix, $dbSettings[$dbSettings['type']]??[]); } + private static function getSessionId(MTProto $madelineProto): string + { + $result = $madelineProto->getSelf()['id'] ?? null; + if (!$result) { + $result = 'tmp_'; + $result .= str_replace('0','', spl_object_hash($madelineProto)); + } + return (string) $result; + } + } \ No newline at end of file diff --git a/src/danog/MadelineProto/Db/DbType.php b/src/danog/MadelineProto/Db/DbType.php index bab9d584..f816a778 100644 --- a/src/danog/MadelineProto/Db/DbType.php +++ b/src/danog/MadelineProto/Db/DbType.php @@ -4,5 +4,5 @@ namespace danog\MadelineProto\Db; interface DbType { - static function getInstance(string $name, $value, string $tablePrefix, array $settings): self; + static function getInstance(string $name, $value = null, string $tablePrefix = '', array $settings = []): self; } \ No newline at end of file diff --git a/src/danog/MadelineProto/Db/MemoryArray.php b/src/danog/MadelineProto/Db/MemoryArray.php index 39d94ac1..18132189 100644 --- a/src/danog/MadelineProto/Db/MemoryArray.php +++ b/src/danog/MadelineProto/Db/MemoryArray.php @@ -13,7 +13,7 @@ class MemoryArray extends \ArrayIterator implements DbArray parent::__construct((array) $array, $flags | self::STD_PROP_LIST); } - public static function getInstance(string $name, $value, string $tablePrefix, array $settings): DbArray + public static function getInstance(string $name, $value = null, string $tablePrefix = '', array $settings = []): DbArray { if ($value instanceof DbArray) { $value = $value->getArrayCopy(); @@ -21,26 +21,36 @@ class MemoryArray extends \ArrayIterator implements DbArray return new static($value); } - public static function getDbConnection(array $settings) + public function offsetExists($offset): Promise { - return null; + return call(fn() => parent::offsetExists($offset)); } - public function offsetGetAsync(string $offset): Promise + public function offsetGet($offset): Promise { - return call(fn() => $this->offsetGet($offset)); + return call(fn() => parent::offsetGet($offset)); } - public function offsetSetAsync(string $offset, $value): Promise + public function offsetUnset($offset): Promise { - return call(fn() => $this->offsetSet($offset, $value)); + return call(fn() => parent::offsetUnset($offset)); + } + + public function count(): Promise + { + return call(fn() => parent::count()); + } + + public function getArrayCopy(): array + { + return parent::getArrayCopy(); } public function getIterator(): Producer { return new Producer(function (callable $emit) { - foreach ($this as $value) { - yield $emit($value); + foreach ($this as $key => $value) { + yield $emit([$key, $value]); } }); } diff --git a/src/danog/MadelineProto/Db/Mysql.php b/src/danog/MadelineProto/Db/Mysql.php index 7f7f60cd..07c9051b 100644 --- a/src/danog/MadelineProto/Db/Mysql.php +++ b/src/danog/MadelineProto/Db/Mysql.php @@ -4,6 +4,7 @@ namespace danog\MadelineProto\Db; use Amp\Mysql\ConnectionConfig; use Amp\Mysql\Pool; +use function Amp\call; use function Amp\Mysql\Pool; use function Amp\Promise\wait; @@ -52,12 +53,17 @@ class Mysql * @throws \Amp\Sql\FailureException * @throws \Throwable */ - private static function createDb(ConnectionConfig $config) { - $db = $config->getDatabase(); - wait(pool($config->withDatabase(null))->query(" - CREATE DATABASE IF NOT EXISTS `{$db}` - CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci - ")); + private static function createDb(ConnectionConfig $config) + { + wait(call(function() use($config) { + $db = $config->getDatabase(); + $connection = pool($config->withDatabase(null)); + yield $connection->query(" + CREATE DATABASE IF NOT EXISTS `{$db}` + CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci + "); + $connection->close(); + })); } diff --git a/src/danog/MadelineProto/Db/MysqlArray.php b/src/danog/MadelineProto/Db/MysqlArray.php index eccdc090..cbb3583e 100644 --- a/src/danog/MadelineProto/Db/MysqlArray.php +++ b/src/danog/MadelineProto/Db/MysqlArray.php @@ -18,8 +18,6 @@ class MysqlArray implements DbArray private string $table; private array $settings; private Pool $db; - private ?string $key = null; - private $current; public function __serialize(): array { @@ -42,7 +40,7 @@ class MysqlArray implements DbArray } - public static function getInstance(string $name, $value, string $tablePrefix, array $settings): DbType + public static function getInstance(string $name, $value = null, string $tablePrefix = '', array $settings = []): DbType { $instance = new static(); @@ -52,13 +50,18 @@ class MysqlArray implements DbArray $instance->ttl = $settings['cache_ttl'] ?? $instance->ttl; if ($value instanceof static) { - if ($instance->table !== $value->table) { + if ( + mb_strpos($value->table, 'tmp') === 0 && + mb_strpos($instance->table, 'tmp') !== 0 + ) { $instance->renameTable($value->table, $instance->table); + } elseif (mb_strpos($instance->table, 'tmp') === 0){ + $instance->table = $value->table; } } $instance->prepareTable(); - Loop::defer(function() use($value, $instance){ + Loop::defer(static function() use($value, $instance) { if (!empty($value) && !$value instanceof static) { Logger::log('Converting database.', Logger::ERROR); if ($value instanceof DbArray) { @@ -81,7 +84,6 @@ class MysqlArray implements DbArray } }); - return $instance; } @@ -94,33 +96,16 @@ class MysqlArray implements DbArray * The offset being checked. *

* - * @return bool true if the offset exists, otherwise false + * @return Promise true if the offset exists, otherwise false * @throws \Throwable */ - public function offsetExists($index) + public function offsetExists($index): Promise { - return $this->offsetGet($index) !== null; - } - - /** - * Get value for an offset - * - * @link https://php.net/manual/en/arrayiterator.offsetget.php - * - * @param string $index

- * The offset to get the value from. - *

- * - * @return mixed The value at offset index. - * @throws \Throwable - */ - public function offsetGet($index) - { - return wait($this->offsetGetAsync($index)); + return call(fn() => yield $this->offsetGet($index) !== null); } - public function offsetGetAsync(string $offset): Promise + public function offsetGet($offset): Promise { return call(function() use($offset) { if ($cached = $this->getCache($offset)) { @@ -150,19 +135,15 @@ class MysqlArray implements DbArray *

* @param $value * - * @return void + * @return Promise * @throws \Throwable */ - public function offsetSet($index, $value) - { - wait($this->offsetSetAsync($index, $value)); - } - public function offsetSetAsync($index, $value): Promise + public function offsetSet($index, $value): void { $this->setCache($index, $value); - return $this->request(" + $this->request(" INSERT INTO `{$this->table}` SET `key` = :index, `value` = :value ON DUPLICATE KEY UPDATE `value` = :value @@ -183,14 +164,14 @@ class MysqlArray implements DbArray * The offset to unset. *

* - * @return void + * @return Promise * @throws \Throwable */ - public function offsetUnset($index) + public function offsetUnset($index): Promise { $this->unsetCache($index); - $this->syncRequest(" + return $this->request(" DELETE FROM `{$this->table}` WHERE `key` = :index ", @@ -211,7 +192,7 @@ class MysqlArray implements DbArray $rows = $this->syncRequest("SELECT `key`, `value` FROM {$this->table}"); $result = []; foreach ($rows as $row) { - $result[$row['key']] = unserialize($row['value']); + $result[$row['key']] = $this->getValue($row); } return $result; @@ -225,7 +206,7 @@ class MysqlArray implements DbArray while (yield $request->advance()) { $row = $request->getCurrent(); - yield $emit($this->getValue($row)); + yield $emit([$row['key'], $this->getValue($row)]); } }); } @@ -234,40 +215,16 @@ class MysqlArray implements DbArray * Count elements * * @link https://php.net/manual/en/arrayiterator.count.php - * @return int The number of elements or public properties in the associated + * @return Promise The number of elements or public properties in the associated * array or object, respectively. * @throws \Throwable */ - public function count(): int + public function count(): Promise { - $row = $this->syncRequest("SELECT count(`key`) as `count` FROM {$this->table}"); - return $row[0]['count'] ?? 0; - } - - /** - * Rewind array back to the start - * - * @link https://php.net/manual/en/arrayiterator.rewind.php - * @return void - * @throws \Throwable - */ - public function rewind() - { - $this->key = null; - $this->key(); - $this->current = null; - } - - /** - * Return current array entry - * - * @link https://php.net/manual/en/arrayiterator.current.php - * @return mixed The current array entry. - * @throws \Throwable - */ - public function current() - { - return $this->current ?: $this->offsetGet($this->key()); + return call(function(){ + $row = yield $this->request("SELECT count(`key`) as `count` FROM {$this->table}"); + return $row[0]['count'] ?? 0; + }); } private function getValue(array $row) @@ -281,71 +238,6 @@ class MysqlArray implements DbArray return null; } - /** - * Return current array key - * - * @link https://php.net/manual/en/arrayiterator.key.php - * @return string|float|int|bool|null The current array key. - * @throws \Throwable - */ - public function key(): ?string - { - if ($this->key === null) { - $row = $this->syncRequest( - "SELECT `key` FROM {$this->table} ORDER BY `key` LIMIT 1" - ); - $this->key = $row[0]['key'] ?? null; - } - return $this->key; - } - - /** - * Move to next entry - * - * @link https://php.net/manual/en/arrayiterator.next.php - * @return void - * @throws \Throwable - */ - public function next() - { - $row = $this->syncRequest( - "SELECT `key`, `value` FROM {$this->table} WHERE `key` > :key ORDER BY `key` LIMIT 1", - ['key' => $this->key()] - ); - - $this->key = $row[0]['key'] ?? null; - $this->current = $this->getValue($row); - } - - /** - * Check whether array contains more entries - * - * @link https://php.net/manual/en/arrayiterator.valid.php - * @return bool - * @throws \Throwable - */ - public function valid():bool - { - return $this->key !== null; - } - - /** - * Seek to position - * @link https://php.net/manual/en/arrayiterator.seek.php - * @param int $position

- * The position to seek to. - *

- * @return void - */ - public function seek($position) - { - $row = $this->syncRequest( - "SELECT `key` FROM {$this->table} ORDER BY `key` LIMIT 1 OFFSET :position", - ['offset' => $position] - ); - $this->key = $row[0]['key'] ?? $this->key; - } - public static function getDbConnection(array $settings): Pool { return Mysql::getConnection( @@ -384,6 +276,14 @@ class MysqlArray implements DbArray "); } catch (\Throwable $e) { Logger::log("Cant rename table {$from} to {$to}", Logger::WARNING); + + try { + $this->syncRequest(" + DROP TABLE {$from}; + "); + } catch (\Throwable $e) { + Logger::log("Cant drop table {$from}", Logger::WARNING); + } } } diff --git a/src/danog/MadelineProto/InternalDoc.php b/src/danog/MadelineProto/InternalDoc.php index b14eac10..d49a4120 100644 --- a/src/danog/MadelineProto/InternalDoc.php +++ b/src/danog/MadelineProto/InternalDoc.php @@ -4285,11 +4285,12 @@ class InternalDoc extends APIFactory * * @param array $user User info * - * @return void + * @return \Generator + * @throws Exception */ - public function addUser(array $user): void + public function addUser(array $user): \Generator { - $this->API->addUser($user); + yield from $this->API->addUser($user); } /** * Call promise $b after promise $a. @@ -4754,11 +4755,11 @@ class InternalDoc extends APIFactory * * @param mixed $id Chat ID * - * @return integer + * @return \Generator */ - public function fullChatLastUpdated($id): int + public function fullChatLastUpdated($id): \Generator { - return $this->API->fullChatLastUpdated($id); + return yield from $this->API->fullChatLastUpdated($id); } /** * Get info about the logged-in user, not cached. diff --git a/src/danog/MadelineProto/MTProto.php b/src/danog/MadelineProto/MTProto.php index 313bfc18..ab70a34d 100644 --- a/src/danog/MadelineProto/MTProto.php +++ b/src/danog/MadelineProto/MTProto.php @@ -22,6 +22,7 @@ namespace danog\MadelineProto; use Amp\Dns\Resolver; use Amp\File\StatCache; use Amp\Http\Client\HttpClient; +use Amp\Promise; use danog\MadelineProto\Async\AsyncConstruct; use danog\MadelineProto\Db\DbArray; use danog\MadelineProto\Db\DbPropertiesFabric; @@ -288,13 +289,13 @@ class MTProto extends AsyncConstruct implements TLCallback /** * Cache of usernames for chats * - * @var DbArray + * @var DbArray|Promise[] */ public $usernames; /** * Cached parameters for fetching channel participants. * - * @var DbArray + * @var DbArray|Promise[] */ public $channel_participants; /** @@ -312,7 +313,7 @@ class MTProto extends AsyncConstruct implements TLCallback /** * Full chat info database. * - * @var DbArray + * @var DbArray|Promise[] */ public $full_chats; /** @@ -454,7 +455,7 @@ class MTProto extends AsyncConstruct implements TLCallback // Parse and store settings yield from $this->updateSettings($settings, false); $this->logger->logger(Lang::$current_lang['inst_dc'], Logger::ULTRA_VERBOSE); - $this->cleanupProperties(); + yield from $this->cleanupProperties(); // Load rsa keys $this->logger->logger(Lang::$current_lang['load_rsa'], Logger::ULTRA_VERBOSE); $this->rsa_keys = []; @@ -565,7 +566,7 @@ class MTProto extends AsyncConstruct implements TLCallback ]; } - public function initDb(bool $reset = false): void + public function initDb(bool $reset = false): \Generator { foreach ($this->dbProperies as $property => $type) { if ($reset) { @@ -575,18 +576,16 @@ class MTProto extends AsyncConstruct implements TLCallback } } - if (!$reset && count($this->usernames) === 0) { - \Amp\Loop::run(function() { - $this->logger('Filling database cache. This can take few minutes.', Logger::WARNING); - $iterator = $this->chats->getIterator(); - while (yield $iterator->advance()) { - $chat = $iterator->getCurrent(); - if (isset($chat['username'])) { - $this->usernames->offsetSetAsync(\strtolower($chat['username']), $this->getId($chat)); - } + if (!$reset && yield $this->usernames->count() === 0) { + $this->logger('Filling database cache. This can take few minutes.', Logger::WARNING); + $iterator = $this->chats->getIterator(); + while (yield $iterator->advance()) { + [$id, $chat] = $iterator->getCurrent(); + if (isset($chat['username'])) { + $this->usernames[\strtolower($chat['username'])] = $this->getId($chat); } - $this->logger('Cache filled.', Logger::WARNING); - }); + } + $this->logger('Cache filled.', Logger::WARNING); } } @@ -802,13 +801,17 @@ class MTProto extends AsyncConstruct implements TLCallback $this->TL->init($this->settings['tl_schema']['src'], $callbacks); } - $this->initDb(); + yield from $this->initDb(); } + /** * Upgrade MadelineProto instance. * * @return \Generator + * @throws Exception + * @throws RPCErrorException + * @throws \Throwable */ private function upgradeMadelineProto(): \Generator { @@ -831,16 +834,19 @@ class MTProto extends AsyncConstruct implements TLCallback unset($settings['authorization']['rsa_key']); } - $this->initDb(); + yield from $this->initDb(); if (!isset($this->secret_chats)) { $this->secret_chats = []; } - foreach ($this->full_chats as $id => $full) { + $iterator = $this->full_chats->getIterator(); + while (yield $iterator->advance()) { + [$id, $full] = $iterator->getCurrent(); if (isset($full['full'], $full['last_update'])) { $this->full_chats[$id] = ['full' => $full['full'], 'last_update' => $full['last_update']]; } } + foreach ($this->secret_chats as $key => &$chat) { if (!\is_array($chat)) { unset($this->secret_chats[$key]); @@ -942,7 +948,7 @@ class MTProto extends AsyncConstruct implements TLCallback $force = true; } // Cleanup old properties, init new stuffs - $this->cleanupProperties(); + yield from $this->cleanupProperties(); // Update TL callbacks $callbacks = [$this, $this->referenceDatabase]; if (!($this->authorization['user']['bot'] ?? false)) { @@ -1509,9 +1515,9 @@ class MTProto extends AsyncConstruct implements TLCallback * * @internal * - * @return void + * @return \Generator */ - public function resetSession(): void + public function resetSession(): \Generator { if (isset($this->seqUpdater)) { $this->seqUpdater->signal(true); @@ -1544,7 +1550,7 @@ class MTProto extends AsyncConstruct implements TLCallback $this->updates = []; $this->secret_chats = []; - $this->initDb(true); + yield from $this->initDb(true); $this->tos = ['expires' => 0, 'accepted' => true]; $this->referenceDatabase = new ReferenceDatabase($this); diff --git a/src/danog/MadelineProto/MTProtoTools/PeerHandler.php b/src/danog/MadelineProto/MTProtoTools/PeerHandler.php index 31f7f0e6..60bcb749 100644 --- a/src/danog/MadelineProto/MTProtoTools/PeerHandler.php +++ b/src/danog/MadelineProto/MTProtoTools/PeerHandler.php @@ -22,6 +22,7 @@ namespace danog\MadelineProto\MTProtoTools; use Amp\Http\Client\Request; use danog\Decoder\FileId; use danog\Decoder\PhotoSizeSource\PhotoSizeSourceDialogPhoto; +use danog\MadelineProto\Db\DbArray; use const danog\Decoder\PROFILE_PHOTO; @@ -82,26 +83,29 @@ trait PeerHandler { $this->supportUser = $support['user']['id']; } + /** * Add user info. * * @param array $user User info * - * @return void + * @return \Generator + * @throws \danog\MadelineProto\Exception */ - public function addUser(array $user): void + public function addUser(array $user): \Generator { + $existingChat = yield $this->chats[$user['id']]; if (!isset($user['access_hash']) && !($user['min'] ?? false)) { - if (isset($this->chats[$user['id']]['access_hash']) && $this->chats[$user['id']]['access_hash']) { + if (!empty($existingChat['access_hash'])) { $this->logger->logger("No access hash with user {$user['id']}, using backup"); - $user['access_hash'] = $this->chats[$user['id']]['access_hash']; + $user['access_hash'] = $existingChat['access_hash']; } elseif (!isset($this->caching_simple[$user['id']]) && !(isset($user['username']) && isset($this->caching_simple_username[$user['username']]))) { $this->logger->logger("No access hash with user {$user['id']}, trying to fetch by ID..."); if (isset($user['username']) && !isset($this->caching_simple_username[$user['username']])) { $this->caching_possible_username[$user['id']] = $user['username']; } $this->cachePwrChat($user['id'], false, true); - } elseif (isset($user['username']) && !isset($this->chats[$user['id']]) && !isset($this->caching_simple_username[$user['username']])) { + } elseif (isset($user['username']) && !$existingChat && !isset($this->caching_simple_username[$user['username']])) { $this->logger->logger("No access hash with user {$user['id']}, trying to fetch by username..."); $this->cachePwrChat($user['username'], false, true); } else { @@ -111,13 +115,13 @@ trait PeerHandler } switch ($user['_']) { case 'user': - if (!isset($this->chats[$user['id']]) || $this->chats[$user['id']] !== $user) { + if (!$existingChat || $existingChat !== $user) { $this->logger->logger("Updated user {$user['id']}", \danog\MadelineProto\Logger::ULTRA_VERBOSE); - if (($user['min'] ?? false) && isset($this->chats[$user['id']]) && !($this->chats[$user['id']]['min'] ?? false)) { + if (($user['min'] ?? false) && !($existingChat['min'] ?? false)) { $this->logger->logger("{$user['id']} is min, filling missing fields", \danog\MadelineProto\Logger::ULTRA_VERBOSE); - if (isset($this->chats[$user['id']]['access_hash'])) { + if (isset($existingChat['access_hash'])) { $user['min'] = false; - $user['access_hash'] = $this->chats[$user['id']]['access_hash']; + $user['access_hash'] = $existingChat['access_hash']; } } $this->chats[$user['id']] = $user; @@ -138,7 +142,7 @@ trait PeerHandler * * @internal * - * @return void + * @return \Generator */ public function addChat($chat): \Generator { @@ -146,7 +150,8 @@ trait PeerHandler case 'chat': case 'chatEmpty': case 'chatForbidden': - if (!isset($this->chats[-$chat['id']]) || $this->chats[-$chat['id']] !== $chat) { + $existingChat = yield $this->chats[-$chat['id']]; + if (!$existingChat || $existingChat !== $chat) { $this->logger->logger("Updated chat -{$chat['id']}", \danog\MadelineProto\Logger::ULTRA_VERBOSE); $this->chats[-$chat['id']] = $chat; $this->cacheChatUsername(-$chat['id'], $chat); @@ -165,7 +170,7 @@ trait PeerHandler $this->caching_possible_username[$bot_api_id] = $chat['username']; } $this->cachePwrChat($bot_api_id, false, true); - } elseif (isset($chat['username']) && !isset($this->chats[$bot_api_id]) && !isset($this->caching_simple_username[$chat['username']])) { + } elseif (isset($chat['username']) && !(yield $this->chats[$bot_api_id]) && !isset($this->caching_simple_username[$chat['username']])) { $this->logger->logger("No access hash with {$chat['_']} {$bot_api_id}, trying to fetch by username..."); $this->cachePwrChat($chat['username'], false, true); } else { @@ -173,11 +178,12 @@ trait PeerHandler } return; } - if (!isset($this->chats[$bot_api_id]) || $this->chats[$bot_api_id] !== $chat) { + $existingChat = yield $this->chats[$bot_api_id]; + if (!$existingChat || $existingChat !== $chat) { $this->logger->logger("Updated chat {$bot_api_id}", \danog\MadelineProto\Logger::ULTRA_VERBOSE); - if (($chat['min'] ?? false) && isset($this->chats[$bot_api_id]) && !($this->chats[$bot_api_id]['min'] ?? false)) { + if (($chat['min'] ?? false) && $existingChat && !($existingChat['min'] ?? false)) { $this->logger->logger("{$bot_api_id} is min, filling missing fields", \danog\MadelineProto\Logger::ULTRA_VERBOSE); - $newchat = $this->chats[$bot_api_id]; + $newchat = $existingChat; foreach (['title', 'username', 'photo', 'banned_rights', 'megagroup', 'verified'] as $field) { if (isset($chat[$field])) { $newchat[$field] = $chat[$field]; @@ -187,7 +193,8 @@ trait PeerHandler } $this->chats[$bot_api_id] = $chat; $this->cacheChatUsername($bot_api_id, $chat); - if ($this->settings['peer']['full_fetch'] && (!isset($this->full_chats[$bot_api_id]) || $this->full_chats[$bot_api_id]['full']['participants_count'] !== (yield from $this->getFullInfo($bot_api_id))['full']['participants_count'])) { + $fullChat = yield $this->full_chats[$bot_api_id]; + if ($this->settings['peer']['full_fetch'] && (!$fullChat || $fullChat['full']['participants_count'] !== (yield from $this->getFullInfo($bot_api_id))['full']['participants_count'])) { $this->cachePwrChat($bot_api_id, $this->settings['peer']['full_fetch'], true); } } @@ -224,7 +231,9 @@ trait PeerHandler public function peerIsset($id): \Generator { try { - return isset($this->chats[(yield from $this->getInfo($id))['bot_api_id']]); + $info = yield from $this->getInfo($id); + $chatId = $info['bot_api_id']; + return (yield $this->chats[$chatId]) !== null; } catch (\danog\MadelineProto\Exception $e) { return false; } catch (\danog\MadelineProto\RPCErrorException $e) { @@ -481,7 +490,7 @@ trait PeerHandler } $tried_simple = false; if (\is_numeric($id)) { - if (!isset($this->chats[$id])) { + if (! yield $this->chats[$id]) { try { $this->logger->logger("Try fetching {$id} with access hash 0"); $this->caching_simple[$id] = true; @@ -505,15 +514,15 @@ trait PeerHandler $tried_simple = true; } } - if (isset($this->chats[$id])) { - if (($this->chats[$id]['min'] ?? false) && $this->minDatabase->hasPeer($id) && !isset($this->caching_full_info[$id])) { + if (yield $this->chats[$id]) { + if (((yield $this->chats[$id])['min'] ?? false) && $this->minDatabase->hasPeer($id) && !isset($this->caching_full_info[$id])) { $this->caching_full_info[$id] = true; $this->logger->logger("Only have min peer for {$id} in database, trying to fetch full info"); try { if ($id < 0) { - yield from $this->methodCallAsyncRead('channels.getChannels', ['id' => [$this->genAll($this->chats[$id], $folder_id)['InputChannel']]], ['datacenter' => $this->datacenter->curdc]); + yield from $this->methodCallAsyncRead('channels.getChannels', ['id' => [$this->genAll(yield $this->chats[$id], $folder_id)['InputChannel']]], ['datacenter' => $this->datacenter->curdc]); } else { - yield from $this->methodCallAsyncRead('users.getUsers', ['id' => [$this->genAll($this->chats[$id], $folder_id)['InputUser']]], ['datacenter' => $this->datacenter->curdc]); + yield from $this->methodCallAsyncRead('users.getUsers', ['id' => [$this->genAll(yield $this->chats[$id], $folder_id)['InputUser']]], ['datacenter' => $this->datacenter->curdc]); } } catch (\danog\MadelineProto\Exception $e) { $this->logger->logger($e->getMessage(), \danog\MadelineProto\Logger::WARNING); @@ -524,10 +533,10 @@ trait PeerHandler } } try { - return $this->genAll($this->chats[$id], $folder_id); + return $this->genAll(yield $this->chats[$id], $folder_id); } catch (\danog\MadelineProto\Exception $e) { if ($e->getMessage() === 'This peer is not present in the internal peer database') { - unset($this->chats[$id]); + yield $this->chats->offsetUnset($id);/** @uses DbArray::offsetUnset() */ } else { throw $e; } @@ -574,10 +583,10 @@ trait PeerHandler } return yield from $this->getInfo($this->supportUser); } - if ($bot_api_id = $this->usernames[$id] ?? null) { - $chat = $this->chats[$bot_api_id]; + if ($bot_api_id = yield $this->usernames[$id]) { + $chat = yield $this->chats[$bot_api_id]; if (empty($chat['username']) || \strtolower($chat['username']) !== $id) { - unset($this->usernames[$id]); + yield $this->usernames->offsetUnset($id); /** @uses DbArray::offsetUnset() */ } if (isset($chat['username']) && \strtolower($chat['username']) === $id) { @@ -586,9 +595,9 @@ trait PeerHandler $this->logger->logger("Only have min peer for {$bot_api_id} in database, trying to fetch full info"); try { if ($bot_api_id < 0) { - yield from $this->methodCallAsyncRead('channels.getChannels', ['id' => [$this->genAll($this->chats[$bot_api_id], $folder_id)['InputChannel']]], ['datacenter' => $this->datacenter->curdc]); + yield from $this->methodCallAsyncRead('channels.getChannels', ['id' => [$this->genAll(yield $this->chats[$bot_api_id], $folder_id)['InputChannel']]], ['datacenter' => $this->datacenter->curdc]); } else { - yield from $this->methodCallAsyncRead('users.getUsers', ['id' => [$this->genAll($this->chats[$bot_api_id], $folder_id)['InputUser']]], ['datacenter' => $this->datacenter->curdc]); + yield from $this->methodCallAsyncRead('users.getUsers', ['id' => [$this->genAll(yield $this->chats[$bot_api_id], $folder_id)['InputUser']]], ['datacenter' => $this->datacenter->curdc]); } } catch (\danog\MadelineProto\Exception $e) { $this->logger->logger($e->getMessage(), \danog\MadelineProto\Logger::WARNING); @@ -598,7 +607,7 @@ trait PeerHandler unset($this->caching_full_info[$bot_api_id]); } } - return $this->genAll($this->chats[$bot_api_id], $folder_id); + return $this->genAll(yield $this->chats[$bot_api_id], $folder_id); } } @@ -674,11 +683,11 @@ trait PeerHandler * * @param mixed $id Chat ID * - * @return integer + * @return \Generator */ - public function fullChatLastUpdated($id): int + public function fullChatLastUpdated($id): \Generator { - return isset($this->full_chats[$id]['last_update']) ? $this->full_chats[$id]['last_update'] : 0; + return (yield $this->full_chats[$id])['last_update'] ?? 0; } /** * Get full info about peer, returns an FullInfo object. @@ -692,8 +701,8 @@ trait PeerHandler public function getFullInfo($id): \Generator { $partial = (yield from $this->getInfo($id)); - if (\time() - $this->fullChatLastUpdated($partial['bot_api_id']) < (isset($this->settings['peer']['full_info_cache_time']) ? $this->settings['peer']['full_info_cache_time'] : 0)) { - return \array_merge($partial, $this->full_chats[$partial['bot_api_id']]); + if (\time() - (yield from $this->fullChatLastUpdated($partial['bot_api_id'])) < (isset($this->settings['peer']['full_info_cache_time']) ? $this->settings['peer']['full_info_cache_time'] : 0)) { + return \array_merge($partial, yield $this->full_chats[$partial['bot_api_id']]); } switch ($partial['type']) { case 'user': @@ -912,10 +921,10 @@ trait PeerHandler } throw $e; } - if ($cached = $gres['_'] === 'channels.channelParticipantsNotModified') { - $gres = $this->fetchParticipantsCache($channel, $filter, $q, $offset, $limit); + if ($cached = ($gres['_'] === 'channels.channelParticipantsNotModified')) { + $gres = yield from $this->fetchParticipantsCache($channel, $filter, $q, $offset, $limit); } else { - $this->storeParticipantsCache($gres, $channel, $filter, $q, $offset, $limit); + yield from $this->storeParticipantsCache($gres, $channel, $filter, $q, $offset, $limit); } if ($last_count !== -1 && $last_count !== $gres['count']) { $has_more = true; @@ -975,11 +984,10 @@ trait PeerHandler } private function fetchParticipantsCache($channel, $filter, $q, $offset, $limit) { - return $this->channel_participants[$channel['channel_id']][$filter][$q][$offset][$limit]; + return (yield $this->channel_participants[$channel['channel_id']])[$filter][$q][$offset][$limit]; } - private function storeParticipantsCache($gres, $channel, $filter, $q, $offset, $limit) + private function storeParticipantsCache($gres, $channel, $filter, $q, $offset, $limit): \Generator { - //return; unset($gres['users']); $ids = []; foreach ($gres['participants'] as $participant) { @@ -987,13 +995,13 @@ trait PeerHandler } \sort($ids, SORT_NUMERIC); $gres['hash'] = \danog\MadelineProto\Tools::genVectorHash($ids); - $participant = $this->channel_participants[$channel['channel_id']]; + $participant = yield $this->channel_participants[$channel['channel_id']]; $participant[$filter][$q][$offset][$limit] = $gres; $this->channel_participants[$channel['channel_id']] = $participant; } private function getParticipantsHash($channel, $filter, $q, $offset, $limit) { - return isset($this->channel_participants[$channel['channel_id']][$filter][$q][$offset][$limit]) ? $this->channel_participants[$channel['channel_id']][$filter][$q][$offset][$limit]['hash'] : 0; + return (yield $this->channel_participants[$channel['channel_id']])[$filter][$q][$offset][$limit]['hash'] ?? 0; } private function storeDb($res, $force = false): \Generator { @@ -1054,6 +1062,12 @@ trait PeerHandler } } if ($res['_'] === 'contacts.resolvedPeer') { + foreach ($res['chats'] as $chat) { + yield from $this->addChat($chat); + } + foreach ($res['users'] as $user) { + yield from $this->addUser($user); + } return $res; } return false; diff --git a/src/danog/MadelineProto/MTProtoTools/ReferenceDatabase.php b/src/danog/MadelineProto/MTProtoTools/ReferenceDatabase.php index 72dd357b..b877a57c 100644 --- a/src/danog/MadelineProto/MTProtoTools/ReferenceDatabase.php +++ b/src/danog/MadelineProto/MTProtoTools/ReferenceDatabase.php @@ -422,10 +422,10 @@ class ReferenceDatabase implements TLCallback break; // Peer + photo ID case self::PEER_PHOTO_ORIGIN: - if (isset($this->API->full_chats[$origin['peer']]['last_update'])) { - $chat = $this->API->full_chats[$origin['peer']]; - $chat['last_update'] = 0; - $this->API->full_chats[$origin['peer']] = $chat; + $fullChat = yield $this->API->full_chats[$origin['peer']]; + if (isset($fullChat['last_update'])) { + $fullChat['last_update'] = 0; + $this->API->full_chats[$origin['peer']] = $fullChat; } $this->API->getFullInfo($origin['peer']); break; diff --git a/src/danog/MadelineProto/MTProtoTools/UpdateHandler.php b/src/danog/MadelineProto/MTProtoTools/UpdateHandler.php index facb7475..942e4d7c 100644 --- a/src/danog/MadelineProto/MTProtoTools/UpdateHandler.php +++ b/src/danog/MadelineProto/MTProtoTools/UpdateHandler.php @@ -332,7 +332,7 @@ trait UpdateHandler } if (\in_array($update['_'], ['updateUserName', 'updateUserPhone', 'updateUserBlocked', 'updateUserPhoto', 'updateContactRegistered', 'updateContactLink'])) { $id = $this->getId($update); - $chat = $this->full_chats[$id]; + $chat = yield $this->full_chats[$id]; $chat['last_update'] = 0; $this->full_chats[$id] = $chat; yield from $this->getFullInfo($id); diff --git a/src/danog/MadelineProto/Wrappers/DialogHandler.php b/src/danog/MadelineProto/Wrappers/DialogHandler.php index d7de6417..26f0eab1 100644 --- a/src/danog/MadelineProto/Wrappers/DialogHandler.php +++ b/src/danog/MadelineProto/Wrappers/DialogHandler.php @@ -32,13 +32,15 @@ trait DialogHandler { if ($this->authorization['user']['bot']) { $res = []; - foreach ($this->chats as $chat) { + /** @uses DbArray::getIterator() */ + $iterator = $this->chats->getIterator(); + while (yield $iterator->advance()) { + [$id, $chat] = $iterator->getCurrent(); try { $res[] = $this->genAll($chat)['Peer']; } catch (\Throwable $e) { continue; } - } return $res; } diff --git a/src/danog/MadelineProto/Wrappers/Login.php b/src/danog/MadelineProto/Wrappers/Login.php index 64809d66..82536ea9 100644 --- a/src/danog/MadelineProto/Wrappers/Login.php +++ b/src/danog/MadelineProto/Wrappers/Login.php @@ -35,7 +35,7 @@ trait Login public function logout(): \Generator { yield from $this->methodCallAsyncRead('auth.logOut', [], ['datacenter' => $this->datacenter->curdc]); - $this->resetSession(); + yield from $this->resetSession(); $this->logger->logger(\danog\MadelineProto\Lang::$current_lang['logout_ok'], \danog\MadelineProto\Logger::NOTICE); $this->startUpdateSystem(); return true;