From 41f070b33e7461099d5c73f97b64cef7afd01043 Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Fri, 13 Sep 2019 16:56:29 +0200 Subject: [PATCH] Fix --- .../MTProtoTools/MinDatabase.php | 257 ++++++++++++++++++ 1 file changed, 257 insertions(+) create mode 100644 src/danog/MadelineProto/MTProtoTools/MinDatabase.php diff --git a/src/danog/MadelineProto/MTProtoTools/MinDatabase.php b/src/danog/MadelineProto/MTProtoTools/MinDatabase.php new file mode 100644 index 00000000..6143347d --- /dev/null +++ b/src/danog/MadelineProto/MTProtoTools/MinDatabase.php @@ -0,0 +1,257 @@ +. + * + * @author Daniil Gentili + * @copyright 2016-2019 Daniil Gentili + * @license https://opensource.org/licenses/AGPL-3.0 AGPLv3 + * + * @link https://docs.madelineproto.xyz MadelineProto documentation + */ + +namespace danog\MadelineProto\MTProtoTools; + +use danog\MadelineProto\Exception; +use danog\MadelineProto\MTProto; +use danog\MadelineProto\TL\TLCallback; +use danog\MadelineProto\Tools; + +/** + * Manages min peers. + */ +class MinDatabase implements TLCallback +{ + use Tools; + + const SWITCH_CONSTRUCTORS = [ + 'inputChannel', + 'inputUser', + 'inputPeerUser', + 'inputPeerChannel', + ]; + const CATCH_PEERS = [ + 'message', + 'messageService', + 'peerUser', + 'peerChannel', + 'messageEntityMentionName', + + 'messageFwdHeader', + 'messageActionChatCreate', + 'messageActionChatAddUser', + 'messageActionChatDeleteUser', + 'messageActionChatJoinedByLink', + ]; + const ORIGINS = ['message', 'messageService']; + /** + * References indexed by location. + * + * @var array + */ + private $db = []; + /** + * Temporary cache during deserialization. + * + * @var array + */ + private $cache = []; + /** + * Instance of MTProto. + * + * @var \danog\MadelineProto\MTProto + */ + private $API; + + public function __construct(MTProto $API) + { + $this->API = $API; + $this->init(); + } + + public function __wakeup() + { + $this->init(); + } + + public function __sleep() + { + return ['db', 'API']; + } + + public function init() + { + } + + public function getMethodCallbacks(): array + { + return []; + } + + public function getMethodBeforeCallbacks(): array + { + return []; + } + + public function getConstructorCallbacks(): array + { + return \array_merge( + \array_fill_keys(self::CATCH_PEERS, [[$this, 'addPeer']]), + \array_fill_keys(self::ORIGINS, [[$this, 'addOrigin']]) + ); + } + + public function getConstructorBeforeCallbacks(): array + { + return \array_fill_keys(self::ORIGINS, [[$this, 'addOriginContext']]); + } + + public function getConstructorSerializeCallbacks(): array + { + return \array_fill_keys(self::SWITCH_CONSTRUCTORS, [$this, 'populateFrom']); + } + + public function getTypeMismatchCallbacks(): array + { + return []; + } + + public function reset() + { + if ($this->cache) { + $this->API->logger->logger('Found '.\count($this->cache).' pending contexts', \danog\MadelineProto\Logger::ERROR); + $this->cache = []; + } + } + + public function addPeer(array $location) + { + if (!$this->cache) { + $this->API->logger->logger('Trying to add peer out of context, report the following message to @danogentili!', \danog\MadelineProto\Logger::ERROR); + $frames = []; + $previous = ''; + foreach (\debug_backtrace(0) as $k => $frame) { + if (isset($frame['function']) && $frame['function'] === 'deserialize') { + if (isset($frame['args'][1]['subtype'])) { + if ($frame['args'][1]['subtype'] === $previous) { + continue; + } + + $frames[] = $frame['args'][1]['subtype']; + $previous = $frame['args'][1]['subtype']; + } elseif (isset($frame['args'][1]['type'])) { + if ($frame['args'][1]['type'] === '') { + break; + } + + if ($frame['args'][1]['type'] === $previous) { + continue; + } + + $frames[] = $frame['args'][1]['type']; + $previous = $frame['args'][1]['type']; + } + } + } + $frames = \array_reverse($frames); + $tl_trace = \array_shift($frames); + foreach ($frames as $frame) { + $tl_trace .= "['".$frame."']"; + } + $this->API->logger->logger($tl_trace, \danog\MadelineProto\Logger::ERROR); + + return false; + } + $peers = []; + switch ($location['_']) { + case 'messageFwdHeader': + if (isset($location['from_id'])) { + $peers[$location['from_id']] = true; + } + if (isset($location['channel_id'])) { + $peers[$this->API->to_supergroup($location['channel_id'])] = true; + } + break; + case 'messageActionChatCreate': + case 'messageActionChatAddUser': + foreach ($location['users'] as $user) { + $peers[$user] = true; + } + break; + case 'message': + $peers[$this->API->get_id($location['to_id'])] = true; + if (isset($location['from_id'])) { + $peers[$location['from_id']] = true; + } + break; + default: + $peers[$this->API->get_id($location)] = true; + } + $this->API->logger->logger("Caching peer location info from location from {$location['_']}", \danog\MadelineProto\Logger::ULTRA_VERBOSE); + $key = \count($this->cache) - 1; + foreach ($peers as $id => $true) { + $this->cache[$key][$id] = $id; + } + + return true; + } + + public function addOriginContext(string $type) + { + $this->API->logger->logger("Adding peer origin context for {$type}!", \danog\MadelineProto\Logger::ULTRA_VERBOSE); + $this->cache[] = []; + } + + public function addOrigin(array $data = []) + { + $cache = \array_pop($this->cache); + if ($cache === null) { + throw new \danog\MadelineProto\Exception('Trying to add origin with no origin context set'); + } + $origin = []; + switch ($data['_']) { + case 'message': + case 'messageService': + $origin['peer'] = $this->API->get_id($data); + $origin['msg_id'] = $data['id']; + break; + default: + throw new \danog\MadelineProto\Exception("Unknown origin type provided: {$data['_']}"); + } + foreach ($cache as $id) { + $this->db[$id] = $origin; + } + $this->API->logger->logger("Added origin ({$data['_']}) to ".\count($cache).' peer locations', \danog\MadelineProto\Logger::ULTRA_VERBOSE); + } + + public function populateFrom(array $object) + { + if (!($object['min'] ?? false)) { + return $object; + } + $id = $this->API->get_id($object); + if (isset($this->db[$id])) { + $new = \array_merge($object, $this->db[$id]); + $new['_'] .= 'FromMessage'; + $new['peer'] = (yield $this->API->get_info_async($new['peer']))['InputPeer']; + if ($new['peer']['min']) { + throw new Exception('Peer not found in internal min peer database'); + } + return $new; + } + + return $object; + } + + public function __debugInfo() + { + return ['MinDatabase instance '.\spl_object_hash($this)]; + } +}