From b339a9d8495e6e2fa8a2f96c85f0972c0e7183ba Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Wed, 22 May 2019 17:35:16 +0200 Subject: [PATCH] Major fixes and refactoring of wrapper APIs --- .travis.yml | 2 +- composer.json | 4 +- index.php | 2 +- makephar.php | 2 +- secret_bot.php | 2 +- src/danog/MadelineProto/API.php | 187 ++++++----- src/danog/MadelineProto/APIFactory.php | 194 ++++++----- .../MadelineProto/Async/AsyncConstruct.php | 53 +++ src/danog/MadelineProto/Connection.php | 36 ++- src/danog/MadelineProto/DataCenter.php | 8 +- src/danog/MadelineProto/EventHandler.php | 50 +-- src/danog/MadelineProto/Exception.php | 6 +- .../Loop/Connection/CheckLoop.php | 8 +- .../Loop/Connection/ReadLoop.php | 3 +- .../Loop/Connection/UpdateLoop.php | 1 - .../Loop/Connection/WriteLoop.php | 47 ++- src/danog/MadelineProto/MTProto.php | 26 +- .../MTProtoTools/PeerHandler.php | 9 - src/danog/MadelineProto/Magic.php | 2 + .../MadelineProto/MyTelegramOrgWrapper.php | 302 ++++++++---------- src/danog/MadelineProto/Serialization.php | 69 ---- .../Stream/Common/BufferedRawStream.php | 2 - src/danog/MadelineProto/Tools.php | 1 + src/danog/MadelineProto/Wrappers/ApiStart.php | 114 ++++--- .../MadelineProto/Wrappers/ApiTemplates.php | 22 +- src/danog/MadelineProto/Wrappers/Start.php | 80 ++--- .../MadelineProto/Wrappers/Templates.php | 17 +- tests/testing.php | 1 - 28 files changed, 615 insertions(+), 635 deletions(-) create mode 100644 src/danog/MadelineProto/Async/AsyncConstruct.php diff --git a/.travis.yml b/.travis.yml index 5fde2d46..f16c1da4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,6 @@ language: php php: -- '7.1' +- '7.4' before_install: - echo "phar.readonly = 0" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini diff --git a/composer.json b/composer.json index c79e9cfc..49186771 100644 --- a/composer.json +++ b/composer.json @@ -16,7 +16,6 @@ "vlucas/phpdotenv": "^3", "erusev/parsedown": "^1.6", "rollbar/rollbar": "dev-master", - "ext-curl": "*", "ext-mbstring": "*", "ext-json": "*", "ext-xml": "*", @@ -28,7 +27,8 @@ "amphp/websocket-client": "dev-master", "amphp/artax": "^3.0", "amphp/file": "^0.3.5", - "amphp/uri": "^0.1.4" + "amphp/uri": "^0.1.4", + "amphp/byte-stream": "dev-master#bc191a8 as 1.5" }, "require-dev": { "phpdocumentor/reflection-docblock": "^3.1", diff --git a/index.php b/index.php index 30708c1e..bd2f1b6c 100644 --- a/index.php +++ b/index.php @@ -2,7 +2,7 @@ require 'vendor/autoload.php'; -$MadelineProto = new \danog\MadelineProto\API('sessionf.madeline'); +$MadelineProto = new \danog\MadelineProto\API('session.madeline'); $me = $MadelineProto->start(); $me = $MadelineProto->get_self(); diff --git a/makephar.php b/makephar.php index 1ff493b0..0409bbc4 100755 --- a/makephar.php +++ b/makephar.php @@ -29,7 +29,7 @@ if (!isset($backtrace[0]["file"]) || !in_array(basename($backtrace[0]["file"]), die("madeline.phar cannot be required manually: use the automatic loader, instead: https://docs.madelineproto.xyz/docs/INSTALLATION.html#simple".PHP_EOL); } if (isset($backtrace[1]["file"])) { - chdir(dirname($backtrace[1]["file"])); + @chdir(dirname($backtrace[1]["file"])); } if ($contents = file_get_contents("https://phar.madelineproto.xyz/phar.php?v=new")) { file_put_contents($backtrace[0]["file"], $contents); diff --git a/secret_bot.php b/secret_bot.php index c69cf6b0..d6849c76 100755 --- a/secret_bot.php +++ b/secret_bot.php @@ -92,7 +92,7 @@ class EventHandler extends \danog\MadelineProto\EventHandler if (file_exists('.env')) { echo 'Loading .env...'.PHP_EOL; - $dotenv = new Dotenv\Dotenv(getcwd()); + $dotenv = Dotenv\Dotenv::create(getcwd()); $dotenv->load(); } diff --git a/src/danog/MadelineProto/API.php b/src/danog/MadelineProto/API.php index 13ebec12..27e24c27 100644 --- a/src/danog/MadelineProto/API.php +++ b/src/danog/MadelineProto/API.php @@ -19,6 +19,8 @@ namespace danog\MadelineProto; +use Amp\Deferred; + class API extends APIFactory { use \danog\Serializable; @@ -29,11 +31,21 @@ class API extends APIFactory public $API; public $getting_api_id = false; public $my_telegram_org_wrapper; + public $asyncAPIPromise; public function __magic_construct($params = [], $settings = []) { Magic::class_exists(); set_error_handler(['\\danog\\MadelineProto\\Exception', 'ExceptionErrorHandler']); + $deferred = new Deferred; + $this->asyncAPIPromise = $deferred->promise(); + $this->asyncAPIPromise->onResolve(function () { + $this->asyncAPIPromise = null; + }); + $this->setInitPromise($this->__construct_async($params, $settings, $deferred)); + } + public function __construct_async($params, $settings, $deferred) + { if (is_string($params)) { $realpaths = Serialization::realpaths($params); $this->session = $realpaths['file']; @@ -62,7 +74,7 @@ class API extends APIFactory class_exists('\\Volatile'); $tounserialize = str_replace('O:26:"danog\\MadelineProto\\Button":', 'O:35:"danog\\MadelineProto\\TL\\Types\\Button":', $tounserialize); foreach (['RSA', 'TL\\TLMethod', 'TL\\TLConstructor', 'MTProto', 'API', 'DataCenter', 'Connection', 'TL\\Types\\Button', 'TL\\Types\\Bytes', 'APIFactory'] as $class) { - class_exists('\\danog\\MadelineProto\\' . $class); + class_exists('\\danog\\MadelineProto\\'.$class); } $unserialized = \danog\Serialization::unserialize($tounserialize); } catch (\danog\MadelineProto\Exception $e) { @@ -72,11 +84,11 @@ class API extends APIFactory if (defined('MADELINEPROTO_TEST') && MADELINEPROTO_TEST === 'pony') { throw $e; } - + class_exists('\\Volatile'); $tounserialize = str_replace('O:26:"danog\\MadelineProto\\Button":', 'O:35:"danog\\MadelineProto\\TL\\Types\\Button":', $tounserialize); foreach (['RSA', 'TL\\TLMethod', 'TL\\TLConstructor', 'MTProto', 'API', 'DataCenter', 'Connection', 'TL\\Types\\Button', 'TL\\Types\\Bytes', 'APIFactory'] as $class) { - class_exists('\\danog\\MadelineProto\\' . $class); + class_exists('\\danog\\MadelineProto\\'.$class); } Logger::log((string) $e, Logger::ERROR); if (strpos($e->getMessage(), "Erroneous data format for unserializing 'phpseclib\\Math\\BigInteger'") === 0) { @@ -96,16 +108,15 @@ class API extends APIFactory if (isset($unserialized->API)) { $this->API = $unserialized->API; - $this->callFork((function () { - yield $this->API->asyncInitPromise; - $this->API->asyncInitPromise = null; - $this->APIFactory(); - \danog\MadelineProto\Logger::log('Ping...', Logger::ULTRA_VERBOSE); - $pong = yield $this->ping(['ping_id' => 3], ['async' => true]); - \danog\MadelineProto\Logger::log('Pong: ' . $pong['ping_id'], Logger::ULTRA_VERBOSE); - \danog\MadelineProto\Logger::log(\danog\MadelineProto\Lang::$current_lang['madelineproto_ready'], Logger::NOTICE); - })()); $this->APIFactory(); + $deferred->resolve(); + yield $this->API->initAsync(); + $this->APIFactory(); + \danog\MadelineProto\Logger::log('Ping...', Logger::ULTRA_VERBOSE); + $this->asyncInitPromise = null; + $pong = yield $this->ping(['ping_id' => 3], ['async' => true]); + \danog\MadelineProto\Logger::log('Pong: '.$pong['ping_id'], Logger::ULTRA_VERBOSE); + \danog\MadelineProto\Logger::log(\danog\MadelineProto\Lang::$current_lang['madelineproto_ready'], Logger::NOTICE); return; } @@ -113,34 +124,31 @@ class API extends APIFactory $params = $settings; } if (!isset($params['app_info']['api_id']) || !$params['app_info']['api_id']) { - $app = $this->api_start(); + $app = yield $this->api_start_async($params); $params['app_info']['api_id'] = $app['api_id']; $params['app_info']['api_hash'] = $app['api_hash']; } $this->API = new MTProto($params); + $this->APIFactory(); + $deferred->resolve(); \danog\MadelineProto\Logger::log(\danog\MadelineProto\Lang::$current_lang['apifactory_start'], Logger::VERBOSE); - $this->callFork((function () { - yield $this->API->asyncInitPromise; - $this->API->asyncInitPromise = null; - $this->APIFactory(); - \danog\MadelineProto\Logger::log('Ping...', Logger::ULTRA_VERBOSE); - $pong = yield $this->ping(['ping_id' => 3], ['async' => true]); - \danog\MadelineProto\Logger::log('Pong: ' . $pong['ping_id'], Logger::ULTRA_VERBOSE); - \danog\MadelineProto\Logger::log(\danog\MadelineProto\Lang::$current_lang['madelineproto_ready'], Logger::NOTICE); - })()); - $this->APIFactory(); + yield $this->API->initAsync(); + $this->APIFactory(); + $this->asyncInitPromise = null; + \danog\MadelineProto\Logger::log('Ping...', Logger::ULTRA_VERBOSE); + $pong = yield $this->ping(['ping_id' => 3], ['async' => true]); + \danog\MadelineProto\Logger::log('Pong: '.$pong['ping_id'], Logger::ULTRA_VERBOSE); + \danog\MadelineProto\Logger::log(\danog\MadelineProto\Lang::$current_lang['madelineproto_ready'], Logger::NOTICE); } public function async($async) { $this->async = $async; - foreach ($this->API->get_methods_namespaced() as $pair) { - $namespace = key($pair); - $this->{$namespace}->async = $async; - } - if ($this->API->event_handler && class_exists($this->API->event_handler) && is_subclass_of($this->API->event_handler, '\danog\MadelineProto\EventHandler')) { - $this->API->setEventHandler($this->API->event_handler); + if ($this->API) { + if ($this->API->event_handler && class_exists($this->API->event_handler) && is_subclass_of($this->API->event_handler, '\danog\MadelineProto\EventHandler')) { + $this->API->setEventHandler($this->API->event_handler); + } } } @@ -154,7 +162,10 @@ class API extends APIFactory if (\danog\MadelineProto\Magic::$has_thread && is_object(\Thread::getCurrentThread()) || Magic::is_fork()) { return; } - $this->serialize(); + if ($this->asyncInitPromise) { + $this->init(); + } + $this->wait($this->serialize()); //restore_error_handler(); } @@ -163,49 +174,6 @@ class API extends APIFactory return ['API', 'web_api_template', 'getting_api_id', 'my_telegram_org_wrapper']; } - public function &__get($name) - { - if ($name === 'settings') { - $this->API->setdem = true; - - return $this->API->settings; - } - - return $this->API->storage[$name]; - } - - public function __set($name, $value) - { - if ($name === 'settings') { - if ($this->API->phoneConfigWatcherId) { - $this->wait($this->API->phoneConfigWatcherId); - $this->API->phoneConfigWatcherId = null; - } - if (Magic::is_fork() && !Magic::$processed_fork) { - \danog\MadelineProto\Logger::log('Detected fork'); - $this->API->reset_session(); - foreach ($this->API->datacenter->sockets as $id => $datacenter) { - $this->API->close_and_reopen($id); - } - Magic::$processed_fork = true; - } - - return $this->API->__construct(array_replace_recursive($this->API->settings, $value)); - } - - return $this->API->storage[$name] = $value; - } - - public function __isset($name) - { - return isset($this->API->storage[$name]); - } - - public function __unset($name) - { - unset($this->API->storage[$name]); - } - private function from_camel_case($input) { preg_match_all('!([A-Z][A-Z0-9]*(?=$|[A-Z][a-z0-9])|[A-Za-z][a-z0-9]+)!', $input, $matches); @@ -221,10 +189,10 @@ class API extends APIFactory { if ($this->API) { foreach ($this->API->get_method_namespaces() as $namespace) { - $this->{$namespace} = new APIFactory($namespace, $this->API); + $this->{$namespace} = new APIFactory($namespace, $this->API, $this->async); } $methods = get_class_methods($this->API); - foreach ($methods as $key => $method) { + foreach ($methods as $method) { if ($method == 'method_call_async_read') { unset($methods[array_search('method_call', $methods)]); } elseif (stripos($method, 'async') !== false) { @@ -266,6 +234,9 @@ class API extends APIFactory public function get_all_methods() { + if ($this->asyncInitPromise) { + $this->init(); + } $methods = []; foreach ($this->API->methods->by_id as $method) { $methods[] = $method['method']; @@ -274,16 +245,64 @@ class API extends APIFactory return array_merge($methods, get_class_methods($this->API)); } - public function serialize($params = null) + public function serialize($filename = null) { - if ($params === null) { - $params = $this->session; - } - if (empty($params)) { - return; - } - Logger::log(\danog\MadelineProto\Lang::$current_lang['serializing_madelineproto']); + return $this->callFork((function () use ($filename) { + if ($filename === null) { + $filename = $this->session; + } + if (empty($filename)) { + return; + } + Logger::log(\danog\MadelineProto\Lang::$current_lang['serializing_madelineproto']); - return Serialization::serialize($params, $this); + if ($filename == '') { + throw new \danog\MadelineProto\Exception('Empty filename'); + } + if (isset($this->API->setdem) && $this->API->setdem) { + $this->API->setdem = false; + $this->API->__construct($this->API->settings); + } + if ($this->API === null && !$this->getting_api_id) { + return false; + } + if ($this->API && $this->API->asyncInitPromise) { + yield $this->API->initAsync(); + } + $this->serialized = time(); + $realpaths = Serialization::realpaths($filename); + if (!file_exists($realpaths['lockfile'])) { + touch($realpaths['lockfile']); + clearstatcache(); + } + $realpaths['lockfile'] = fopen($realpaths['lockfile'], 'w'); + \danog\MadelineProto\Logger::log('Waiting for exclusive lock of serialization lockfile...'); + flock($realpaths['lockfile'], LOCK_EX); + \danog\MadelineProto\Logger::log('Lock acquired, serializing'); + + try { + if (!$this->getting_api_id) { + $update_closure = $this->API->settings['updates']['callback']; + if ($this->API->settings['updates']['callback'] instanceof \Closure) { + $this->API->settings['updates']['callback'] = [$this->API, 'noop']; + } + $logger_closure = $this->API->settings['logger']['logger_param']; + if ($this->API->settings['logger']['logger_param'] instanceof \Closure) { + $this->API->settings['logger']['logger_param'] = [$this->API, 'noop']; + } + } + $wrote = file_put_contents($realpaths['tempfile'], serialize($this)); + rename($realpaths['tempfile'], $realpaths['file']); + } finally { + if (!$this->getting_api_id) { + $this->API->settings['updates']['callback'] = $update_closure; + $this->API->settings['logger']['logger_param'] = $logger_closure; + } + flock($realpaths['lockfile'], LOCK_UN); + fclose($realpaths['lockfile']); + } + + return $wrote; + })()); } } diff --git a/src/danog/MadelineProto/APIFactory.php b/src/danog/MadelineProto/APIFactory.php index 575475fd..2624c2bd 100644 --- a/src/danog/MadelineProto/APIFactory.php +++ b/src/danog/MadelineProto/APIFactory.php @@ -20,8 +20,9 @@ namespace danog\MadelineProto; use Amp\Promise; +use danog\MadelineProto\Async\AsyncConstruct; -class APIFactory +class APIFactory extends AsyncConstruct { /** * @internal this is a internal property generated by build_docs.php, don't change manually @@ -119,126 +120,121 @@ class APIFactory public $API; public $lua = false; public $async = false; + public $asyncAPIPromise; protected $methods = []; - public function __construct($namespace, $API) + public function __construct($namespace, $API, &$async) { - $this->namespace = $namespace . '.'; + $this->namespace = $namespace.'.'; $this->API = $API; + $this->async = &$async; } public function __call($name, $arguments) { + $yielded = $this->__call_async($name, $arguments); + $async = $this->lua === false && (is_array(end($arguments)) && isset(end($arguments)['async']) ? end($arguments)['async'] : ($this->async && $name !== 'loop')); + if ($async) { + return $yielded; + } + if (!$this->lua) { + return $this->wait($yielded); + } + + try { + $yielded = $this->wait($yielded); + Lua::convert_objects($yielded); + + return $yielded; + } catch (\Throwable $e) { + return ['error_code' => $e->getCode(), 'error' => $e->getMessage()]; + } + } + + public function __call_async($name, $arguments) + { + if ($this->asyncInitPromise) { + yield $this->initAsync(); + } if (Magic::is_fork() && !Magic::$processed_fork) { \danog\MadelineProto\Logger::log('Detected fork'); $this->API->reset_session(); - foreach ($this->API->datacenter->sockets as $id => $datacenter) { - $this->API->close_and_reopen($id); + foreach ($this->API->datacenter->sockets as $datacenter) { + yield $datacenter->reconnect(); } Magic::$processed_fork = true; } - - if ($this->API->setdem) { - $this->API->setdem = false; - $this->API->__construct($this->API->settings); - } - //$this->API->get_config([], ['datacenter' => $this->API->datacenter->curdc]); - if (isset($this->session) && !is_null($this->session) && time() - $this->serialized > $this->API->settings['serialization']['serialization_interval']) { Logger::log("Didn't serialize in a while, doing that now..."); $this->serialize($this->session); } - /* - if ($name !== 'accept_tos' && $name !== 'decline_tos') { - $this->API->check_tos(); - }*/ + if ($this->API->setdem) { + $this->API->setdem = false; + $this->API->__construct($this->API->settings); + yield $this->API->initAsync(); + } + if ($this->API->asyncInitPromise) { + yield $this->API->initAsync(); + } + $lower_name = strtolower($name); + if ($this->namespace !== '' || !isset($this->methods[$lower_name])) { + $name = $this->namespace.$name; + $aargs = isset($arguments[1]) && is_array($arguments[1]) ? $arguments[1] : []; + $aargs['apifactory'] = true; + $aargs['datacenter'] = $this->API->datacenter->curdc; + $args = isset($arguments[0]) && is_array($arguments[0]) ? $arguments[0] : []; - if ($this->lua === false) { - return $this->namespace !== '' || !isset($this->methods[$lower_name]) ? $this->__mtproto_call($this->namespace . $name, $arguments) : $this->__api_call($lower_name, $arguments); - } - - try { - $deserialized = $this->namespace !== '' || !isset($this->methods[$lower_name]) ? $this->__mtproto_call($this->namespace . $name, $arguments) : $this->__api_call($lower_name, $arguments); - - Lua::convert_objects($deserialized); - - return $deserialized; - } catch (\danog\MadelineProto\Exception $e) { - return ['error_code' => $e->getCode(), 'error' => $e->getMessage()]; - } catch (\danog\MadelineProto\RPCErrorException $e) { - return ['error_code' => $e->getCode(), 'error' => $e->getMessage()]; - } catch (\danog\MadelineProto\TL\Exception $e) { - return ['error_code' => $e->getCode(), 'error' => $e->getMessage()]; - } catch (\danog\MadelineProto\NothingInTheSocketException $e) { - return ['error_code' => $e->getCode(), 'error' => $e->getMessage()]; - } catch (\danog\MadelineProto\PTSException $e) { - return ['error_code' => $e->getCode(), 'error' => $e->getMessage()]; - } catch (\danog\MadelineProto\SecurityException $e) { - return ['error_code' => $e->getCode(), 'error' => $e->getMessage()]; - } catch (\danog\MadelineProto\TL\Conversion\Exception $e) { - return ['error_code' => $e->getCode(), 'error' => $e->getMessage()]; - } - } - - public function __api_call($name, $arguments) - { - if ($this->API->asyncInitPromise) { - $async = is_array(end($arguments)) && isset(end($arguments)['async']) ? end($arguments)['async'] : ($this->async && $name !== 'loop'); - if ($async) { - return $this->call((function () use ($name, $arguments) { - yield $this->API->asyncInitPromise; - $this->API->asyncInitPromise = null; - return yield $this->methods[$name](...$arguments); - })()); - } else { - $this->wait($this->API->asyncInitPromise); - $this->API->asyncInitPromise = null; - } - } - $result = $this->methods[$name](...$arguments); - if (is_object($result) && ($result instanceof \Generator || $result instanceof Promise)) { - $async = is_array(end($arguments)) && isset(end($arguments)['async']) ? end($arguments)['async'] : ($this->async && $name !== 'loop'); - if ($async) { - return $result; - } else { - return $this->wait($result); - } - } - - return $result; - } - - public function __mtproto_call($name, $arguments) - { - $aargs = isset($arguments[1]) && is_array($arguments[1]) ? $arguments[1] : []; - $aargs['apifactory'] = true; - $args = isset($arguments[0]) && is_array($arguments[0]) ? $arguments[0] : []; - - $async = isset(end($arguments)['async']) ? end($arguments)['async'] : $this->async; - - if ($this->API->asyncInitPromise) { - if ($async) { - return $this->call((function () use ($name, $args, $aargs) { - yield $this->API->asyncInitPromise; - $this->API->asyncInitPromise = null; - $aargs['datacenter'] = $this->API->datacenter->curdc; - return yield $this->API->method_call_async_read($name, $args, $aargs); - ; - })()); - } else { - $this->wait($this->API->asyncInitPromise); - $this->API->asyncInitPromise = null; - } - } - $aargs['datacenter'] = $this->API->datacenter->curdc; - $res = $this->API->method_call_async_read($name, $args, $aargs); - - if ($async) { - return $res; + return yield $this->API->method_call_async_read($name, $args, $aargs); } else { - return $this->wait($res); + return yield $this->methods[$lower_name](...$arguments); } } + + public function &__get($name) + { + if ($this->asyncAPIPromise) { + $this->wait($this->asyncAPIPromise); + } + if ($name === 'settings') { + $this->API->setdem = true; + + return $this->API->settings; + } + + return $this->API->storage[$name]; + } + + public function __set($name, $value) + { + if ($this->asyncAPIPromise) { + $this->wait($this->asyncAPIPromise); + } + if ($name === 'settings') { + if ($this->API->asyncInitPromise) { + $this->API->init(); + } + return $this->API->__construct(array_replace_recursive($this->API->settings, $value)); + } + + return $this->API->storage[$name] = $value; + } + + public function __isset($name) + { + if ($this->asyncAPIPromise) { + $this->wait($this->asyncAPIPromise); + } + return isset($this->API->storage[$name]); + } + + public function __unset($name) + { + if ($this->asyncAPIPromise) { + $this->wait($this->asyncAPIPromise); + } + unset($this->API->storage[$name]); + } + } diff --git a/src/danog/MadelineProto/Async/AsyncConstruct.php b/src/danog/MadelineProto/Async/AsyncConstruct.php new file mode 100644 index 00000000..c6430c28 --- /dev/null +++ b/src/danog/MadelineProto/Async/AsyncConstruct.php @@ -0,0 +1,53 @@ +. + * + * @author Daniil Gentili + * @copyright 2016-2018 Daniil Gentili + * @license https://opensource.org/licenses/AGPL-3.0 AGPLv3 + * + * @link https://docs.madelineproto.xyz MadelineProto documentation + */ + +namespace danog\MadelineProto\Async; + +use danog\MadelineProto\Tools; + +/** + * Async constructor class. + * + * Manages asynchronous construction and wakeup of classes + * + * @author Daniil Gentili + */ +class AsyncConstruct +{ + use Tools; + public $asyncInitPromise; + public function init() + { + if ($this->asyncInitPromise) { + $this->wait($this->asyncInitPromise); + } + } + public function initAsync() + { + if ($this->asyncInitPromise) { + yield $this->asyncInitPromise; + } + } + public function setInitPromise($promise) + { + $this->asyncInitPromise = $this->call($promise); + $this->asyncInitPromise->onResolve(function () { + $this->asyncInitPromise = null; + }); + } +} diff --git a/src/danog/MadelineProto/Connection.php b/src/danog/MadelineProto/Connection.php index 445e05ad..20079b9a 100644 --- a/src/danog/MadelineProto/Connection.php +++ b/src/danog/MadelineProto/Connection.php @@ -94,7 +94,6 @@ class Connection return $this->ctx; } - /** * Connect function. * @@ -157,7 +156,6 @@ class Connection } } - public function sendMessage($message, $flush = true) { $deferred = new Deferred(); @@ -201,6 +199,7 @@ class Connection public function disconnect() { + $this->API->logger->logger("Disconnecting from DC {$this->datacenter}"); $this->old = true; foreach (['reader', 'writer', 'checker', 'waiter', 'updater'] as $loop) { if (isset($this->{$loop}) && $this->{$loop}) { @@ -210,6 +209,7 @@ class Connection if ($this->stream) { $this->stream->disconnect(); } + $this->API->logger->logger("Disconnected from DC {$this->datacenter}"); } public function reconnect(): \Generator @@ -226,12 +226,17 @@ class Connection $dc_config_number = isset($API->settings['connection_settings'][$datacenter]) ? $datacenter : 'all'; $timeout = $API->settings['connection_settings'][$dc_config_number]['timeout']; + $pfs = $API->settings['connection_settings'][$dc_config_number]['pfs']; + foreach ($this->new_outgoing as $message_id) { if (isset($this->outgoing_messages[$message_id]['sent']) && $this->outgoing_messages[$message_id]['sent'] + $timeout < time() && ($this->temp_auth_key === null) === $this->outgoing_messages[$message_id]['unencrypted'] && $this->outgoing_messages[$message_id]['_'] !== 'msgs_state_req' ) { + if ($pfs && !isset($this->temp_auth_key['bound']) && $this->outgoing_messages[$message_id]['_'] !== 'auth.bindTempAuthKey') { + continue; + } return true; } } @@ -239,6 +244,33 @@ class Connection return false; } + public function getPendingCalls() + { + $API = $this->API; + $datacenter = $this->datacenter; + + $dc_config_number = isset($API->settings['connection_settings'][$datacenter]) ? $datacenter : 'all'; + $timeout = $API->settings['connection_settings'][$dc_config_number]['timeout']; + $pfs = $API->settings['connection_settings'][$dc_config_number]['pfs']; + + $result = []; + foreach ($this->new_outgoing as $message_id) { + if (isset($this->outgoing_messages[$message_id]['sent']) + && $this->outgoing_messages[$message_id]['sent'] + $timeout < time() + && ($this->temp_auth_key === null) === $this->outgoing_messages[$message_id]['unencrypted'] + && $this->outgoing_messages[$message_id]['_'] !== 'msgs_state_req' + ) { + if ($pfs && !isset($this->temp_auth_key['bound']) && $this->outgoing_messages[$message_id]['_'] !== 'auth.bindTempAuthKey') { + continue; + } + + $result[] = $message_id; + } + } + + return $result; + } + public function getName(): string { return __CLASS__; diff --git a/src/danog/MadelineProto/DataCenter.php b/src/danog/MadelineProto/DataCenter.php index d67e4708..0aa3c130 100644 --- a/src/danog/MadelineProto/DataCenter.php +++ b/src/danog/MadelineProto/DataCenter.php @@ -38,6 +38,7 @@ use danog\MadelineProto\Stream\Transport\DefaultStream; use danog\MadelineProto\Stream\Transport\WssStream; use danog\MadelineProto\Stream\Transport\WsStream; use danog\MadelineProto\TL\Conversion\Exception; +use Amp\Artax\Cookie\ArrayCookieJar; /** * Manages datacenters. @@ -67,12 +68,13 @@ class DataCenter if ($socket instanceof Connection && !strpos($key, '_bk')) { $this->API->logger->logger(sprintf(\danog\MadelineProto\Lang::$current_lang['dc_con_stop'], $key), \danog\MadelineProto\Logger::VERBOSE); $socket->old = true; + $socket->setExtra($this->API); $socket->disconnect(); } else { unset($this->sockets[$key]); } } - $this->HTTPClient = new DefaultClient(null, new HttpSocketPool(new ProxySocketPool($this))); + $this->HTTPClient = new DefaultClient(new ArrayCookieJar, new HttpSocketPool(new ProxySocketPool($this))); } public function rawConnectAsync(string $uri, CancellationToken $token = null, ClientConnectContext $ctx = null): \Generator { @@ -170,10 +172,10 @@ class DataCenter default: throw new Exception(\danog\MadelineProto\Lang::$current_lang['protocol_invalid']); } - if ($this->settings[$dc_config_number]['obfuscated'] && !in_array($default[1][0], [HttpsStream::getName(), HttpStream::getName()])) { + if ($this->settings[$dc_config_number]['obfuscated'] && !in_array($default[2][0], [HttpsStream::getName(), HttpStream::getName()])) { $default = [[DefaultStream::getName(), []], [BufferedRawStream::getName(), []], [ObfuscatedStream::getName(), []], end($default)]; } - if ($this->settings[$dc_config_number]['transport'] && !in_array($default[1][0], [HttpsStream::getName(), HttpStream::getName()])) { + if ($this->settings[$dc_config_number]['transport'] && !in_array($default[2][0], [HttpsStream::getName(), HttpStream::getName()])) { switch ($this->settings[$dc_config_number]['transport']) { case 'tcp': if ($this->settings[$dc_config_number]['obfuscated']) { diff --git a/src/danog/MadelineProto/EventHandler.php b/src/danog/MadelineProto/EventHandler.php index 849763b6..cc890820 100644 --- a/src/danog/MadelineProto/EventHandler.php +++ b/src/danog/MadelineProto/EventHandler.php @@ -24,54 +24,10 @@ class EventHandler extends APIFactory public function __construct($MadelineProto) { $this->API = $MadelineProto->API; - $this->async = $MadelineProto->async; - $this->methods = $MadelineProto->methods; + $this->async = &$MadelineProto->async; + $this->methods = &$MadelineProto->methods; foreach ($this->API->get_method_namespaces() as $namespace) { - $this->{$namespace} = new APIFactory($namespace, $this->API); - $this->{$namespace}->async = $MadelineProto->async; + $this->{$namespace} = new APIFactory($namespace, $this->API, $this->async); } } - - public function &__get($name) - { - if ($name === 'settings') { - $this->API->setdem = true; - - return $this->API->settings; - } - - return $this->API->storage[$name]; - } - - public function __set($name, $value) - { - if ($name === 'settings') { - if ($this->API->phoneConfigWatcherId) { - $this->wait($this->API->phoneConfigWatcherId); - $this->API->phoneConfigWatcherId = null; - } - if (Magic::is_fork() && !Magic::$processed_fork) { - \danog\MadelineProto\Logger::log('Detected fork'); - $this->API->reset_session(); - foreach ($this->API->datacenter->sockets as $id => $datacenter) { - $this->API->close_and_reopen($id); - } - Magic::$processed_fork = true; - } - - return $this->API->__construct(array_replace_recursive($this->API->settings, $value)); - } - - return $this->API->storage[$name] = $value; - } - - public function __isset($name) - { - return isset($this->API->storage[$name]); - } - - public function __unset($name) - { - unset($this->API->storage[$name]); - } } diff --git a/src/danog/MadelineProto/Exception.php b/src/danog/MadelineProto/Exception.php index 0e75b07b..905c9064 100644 --- a/src/danog/MadelineProto/Exception.php +++ b/src/danog/MadelineProto/Exception.php @@ -26,11 +26,7 @@ class Exception extends \Exception public function __toString() { - $result = $this->file === 'MadelineProto' ? $this->message : '\\danog\\MadelineProto\\Exception'.($this->message !== '' ? ': ' : '').$this->message.' in '.$this->file.':'.$this->line.PHP_EOL.\danog\MadelineProto\Magic::$revision.PHP_EOL.'TL Trace (YOU ABSOLUTELY MUST READ THE TEXT BELOW):'.PHP_EOL.$this->getTLTrace(); - if (php_sapi_name() !== 'cli') { - $result = str_replace(PHP_EOL, '
'.PHP_EOL, $result); - } - return $result; + return $this->file === 'MadelineProto' ? $this->message : '\\danog\\MadelineProto\\Exception'.($this->message !== '' ? ': ' : '').$this->message.' in '.$this->file.':'.$this->line.PHP_EOL.\danog\MadelineProto\Magic::$revision.PHP_EOL.'TL Trace (YOU ABSOLUTELY MUST READ THE TEXT BELOW):'.PHP_EOL.$this->getTLTrace(); } public function __construct($message = null, $code = 0, self $previous = null, $file = null, $line = null) diff --git a/src/danog/MadelineProto/Loop/Connection/CheckLoop.php b/src/danog/MadelineProto/Loop/Connection/CheckLoop.php index d3f66e0e..6f83febc 100644 --- a/src/danog/MadelineProto/Loop/Connection/CheckLoop.php +++ b/src/danog/MadelineProto/Loop/Connection/CheckLoop.php @@ -38,7 +38,9 @@ class CheckLoop extends ResumableSignalLoop $this->startedLoop(); $API->logger->logger("Entered check loop in DC {$datacenter}", Logger::ULTRA_VERBOSE); - $timeout = $API->settings['connection_settings'][isset($API->settings['connection_settings'][$datacenter]) ? $datacenter : 'all']['timeout']; + $dc_config_number = isset($API->settings['connection_settings'][$datacenter]) ? $datacenter : 'all'; + + $timeout = $API->settings['connection_settings'][$dc_config_number]['timeout']; while (true) { while (empty($connection->new_outgoing)) { if (yield $this->waitSignal($this->pause())) { @@ -52,7 +54,7 @@ class CheckLoop extends ResumableSignalLoop if ($connection->hasPendingCalls()) { $last_recv = $connection->get_max_id(true); if ($connection->temp_auth_key !== null) { - $message_ids = array_values($connection->new_outgoing); + $message_ids = $connection->getPendingCalls();//array_values($connection->new_outgoing); $deferred = new Deferred(); $deferred->promise()->onResolve( function ($e, $result) use ($message_ids, $API, $connection, $datacenter) { @@ -109,6 +111,7 @@ class CheckLoop extends ResumableSignalLoop } ); $list = ''; + // Don't edit this here pls foreach ($message_ids as $message_id) { $list .= $connection->outgoing_messages[$message_id]['_'].', '; } @@ -126,7 +129,6 @@ class CheckLoop extends ResumableSignalLoop } $connection->writer->resume(); } - //$t = time(); if (yield $this->waitSignal($this->pause($timeout))) { $API->logger->logger("Exiting check loop in DC $datacenter"); $this->exitedLoop(); diff --git a/src/danog/MadelineProto/Loop/Connection/ReadLoop.php b/src/danog/MadelineProto/Loop/Connection/ReadLoop.php index 88306e9f..7a4cca3a 100644 --- a/src/danog/MadelineProto/Loop/Connection/ReadLoop.php +++ b/src/danog/MadelineProto/Loop/Connection/ReadLoop.php @@ -107,7 +107,7 @@ class ReadLoop extends SignalLoop } $this->startedLoop(); if ($this->API->is_http($datacenter)) { - $this->API->datacenter->sockets[$datacenter]->waiter->resume(); + Loop::defer([$connection->waiter, 'resume']); } } } @@ -118,6 +118,7 @@ class ReadLoop extends SignalLoop $datacenter = $this->datacenter; $connection = $this->connection; if (isset($this->connection->old)) { + $API->logger->logger("Not reading because connection is old"); throw new NothingInTheSocketException(); } diff --git a/src/danog/MadelineProto/Loop/Connection/UpdateLoop.php b/src/danog/MadelineProto/Loop/Connection/UpdateLoop.php index 9db29dc3..cd3d6c7e 100644 --- a/src/danog/MadelineProto/Loop/Connection/UpdateLoop.php +++ b/src/danog/MadelineProto/Loop/Connection/UpdateLoop.php @@ -35,7 +35,6 @@ class UpdateLoop extends ResumableSignalLoop { $API = $this->API; $datacenter = $this->datacenter; - $connection = $this->connection; if (!$this->API->settings['updates']['handle_updates']) { yield new Success(0); diff --git a/src/danog/MadelineProto/Loop/Connection/WriteLoop.php b/src/danog/MadelineProto/Loop/Connection/WriteLoop.php index 3ede1f80..26b27480 100644 --- a/src/danog/MadelineProto/Loop/Connection/WriteLoop.php +++ b/src/danog/MadelineProto/Loop/Connection/WriteLoop.php @@ -18,13 +18,13 @@ namespace danog\MadelineProto\Loop\Connection; -use Amp\Coroutine; use Amp\Success; use danog\MadelineProto\Connection; use danog\MadelineProto\Logger; use danog\MadelineProto\Loop\Impl\ResumableSignalLoop; use danog\MadelineProto\MTProtoTools\Crypt; use danog\MadelineProto\Tools; +use danog\MadelineProto\Magic; /** * Socket write loop. @@ -44,7 +44,7 @@ class WriteLoop extends ResumableSignalLoop $this->startedLoop(); $API->logger->logger("Entered write loop in DC {$datacenter}", Logger::ULTRA_VERBOSE); - + $please_wait = false; while (true) { if (empty($connection->pending_outgoing) || $please_wait) { @@ -101,7 +101,7 @@ class WriteLoop extends ResumableSignalLoop $pad_length = -$length & 15; $pad_length += 16 * $this->random_int($modulus = 16); - + $pad = $this->random($pad_length); $buffer = yield $connection->stream->getWriteBuffer(8 + 8 + 4 + $pad_length + $length); @@ -157,18 +157,16 @@ class WriteLoop extends ResumableSignalLoop break; } } - if ($API->is_http($datacenter) && !$has_http_wait) { - //$connection->pending_outgoing[$connection->pending_outgoing_key++] = ['_' => 'http_wait', 'serialized_body' => $this->API->serialize_object(['type' => ''], ['_' => 'http_wait', 'max_wait' => $API->settings['connection_settings'][$dc_config_number]['timeout'] * 1000 - 100, 'wait_after' => 0, 'max_delay' => 0], 'http_wait'), 'content_related' => true, 'unencrypted' => false, 'method' => true]; $connection->pending_outgoing[$connection->pending_outgoing_key++] = ['_' => 'http_wait', 'serialized_body' => yield $this->API->serialize_object_async(['type' => ''], ['_' => 'http_wait', 'max_wait' => 30000, 'wait_after' => 0, 'max_delay' => 1], 'http_wait'), 'content_related' => true, 'unencrypted' => false, 'method' => true]; $connection->pending_outgoing_key %= Connection::PENDING_MAX; - $has_http_wait = true; } $total_length = 0; $count = 0; ksort($connection->pending_outgoing); + $skipped = false; foreach ($connection->pending_outgoing as $k => $message) { if ($message['unencrypted']) { continue; @@ -177,8 +175,9 @@ class WriteLoop extends ResumableSignalLoop unset($connection->pending_outgoing[$k]); continue; } - if ($API->settings['connection_settings'][$dc_config_number]['pfs'] && !isset($connection->temp_auth_key['bound']) && !strpos($datacenter, 'cdn') && $message['_'] !== 'auth.bindTempAuthKey') { + if ($API->settings['connection_settings'][$dc_config_number]['pfs'] && !isset($connection->temp_auth_key['bound']) && !strpos($datacenter, 'cdn') && !in_array($message['_'], ['http_wait', 'auth.bindTempAuthKey']) && $message['method']) { $API->logger->logger("Skipping {$message['_']} due to unbound keys in DC {$datacenter}"); + $skipped = true; continue; } @@ -191,7 +190,6 @@ class WriteLoop extends ResumableSignalLoop $MTmessage = ['_' => 'MTmessage', 'msg_id' => $message_id, 'body' => $body, 'seqno' => $connection->generate_out_seq_no($message['content_related'])]; if (isset($message['method']) && $message['method'] && $message['_'] !== 'http_wait') { - if ((!isset($connection->temp_auth_key['connection_inited']) || $connection->temp_auth_key['connection_inited'] === false) && $message['_'] !== 'auth.bindTempAuthKey') { $API->logger->logger(sprintf(\danog\MadelineProto\Lang::$current_lang['write_client_info'], $message['_']), \danog\MadelineProto\Logger::NOTICE); $MTmessage['body'] = yield $API->serialize_method_async( @@ -201,15 +199,15 @@ class WriteLoop extends ResumableSignalLoop 'query' => yield $API->serialize_method_async( 'initConnection', [ - 'api_id' => $API->settings['app_info']['api_id'], - 'api_hash' => $API->settings['app_info']['api_hash'], - 'device_model' => strpos($datacenter, 'cdn') === false ? $API->settings['app_info']['device_model'] : 'n/a', - 'system_version' => strpos($datacenter, 'cdn') === false ? $API->settings['app_info']['system_version'] : 'n/a', - 'app_version' => $API->settings['app_info']['app_version'], + 'api_id' => $API->settings['app_info']['api_id'], + 'api_hash' => $API->settings['app_info']['api_hash'], + 'device_model' => strpos($datacenter, 'cdn') === false ? $API->settings['app_info']['device_model'] : 'n/a', + 'system_version' => strpos($datacenter, 'cdn') === false ? $API->settings['app_info']['system_version'] : 'n/a', + 'app_version' => $API->settings['app_info']['app_version'], 'system_lang_code' => $API->settings['app_info']['lang_code'], - 'lang_code' => $API->settings['app_info']['lang_code'], - 'lang_pack' => $API->settings['app_info']['lang_pack'], - 'query' => $MTmessage['body'], + 'lang_code' => $API->settings['app_info']['lang_code'], + 'lang_pack' => $API->settings['app_info']['lang_pack'], + 'query' => $MTmessage['body'], ] ), ] @@ -230,12 +228,12 @@ class WriteLoop extends ResumableSignalLoop } /* if ($API->settings['requests']['gzip_encode_if_gt'] !== -1 && ($l = strlen($MTmessage['body'])) > $API->settings['requests']['gzip_encode_if_gt']) { - if (($g = strlen($gzipped = gzencode($MTmessage['body']))) < $l) { - $MTmessage['body'] = yield $API->serialize_object_async(['type' => 'gzip_packed'], ['packed_data' => $gzipped], 'gzipped data'); - $API->logger->logger('Using GZIP compression for ' . $message['_'] . ', saved ' . ($l - $g) . ' bytes of data, reduced call size by ' . $g * 100 / $l . '%', \danog\MadelineProto\Logger::ULTRA_VERBOSE); - } - unset($gzipped); - }*/ + if (($g = strlen($gzipped = gzencode($MTmessage['body']))) < $l) { + $MTmessage['body'] = yield $API->serialize_object_async(['type' => 'gzip_packed'], ['packed_data' => $gzipped], 'gzipped data'); + $API->logger->logger('Using GZIP compression for ' . $message['_'] . ', saved ' . ($l - $g) . ' bytes of data, reduced call size by ' . $g * 100 / $l . '%', \danog\MadelineProto\Logger::ULTRA_VERBOSE); + } + unset($gzipped); + }*/ } } $body_length = strlen($MTmessage['body']); @@ -311,7 +309,7 @@ class WriteLoop extends ResumableSignalLoop if ($has_http_wait) { $connection->last_http_wait = $sent; - } elseif ($API->isAltervista()) { + } elseif (Magic::$altervista) { $connection->last_http_wait = PHP_INT_MAX; } @@ -332,8 +330,9 @@ class WriteLoop extends ResumableSignalLoop } //if (!empty($connection->pending_outgoing)) $connection->select(); - } while (!empty($connection->pending_outgoing)); + } while (!empty($connection->pending_outgoing) && !$skipped); $connection->pending_outgoing_key = 0; + return $skipped; } } diff --git a/src/danog/MadelineProto/MTProto.php b/src/danog/MadelineProto/MTProto.php index 2d2f4321..c30d456a 100644 --- a/src/danog/MadelineProto/MTProto.php +++ b/src/danog/MadelineProto/MTProto.php @@ -26,11 +26,12 @@ use danog\MadelineProto\Stream\MTProtoTransport\HttpsStream; use danog\MadelineProto\Stream\MTProtoTransport\HttpStream; use danog\MadelineProto\TL\TLCallback; use danog\MadelineProto\MTProtoTools\CombinedUpdatesState; +use danog\MadelineProto\Async\AsyncConstruct; /** * Manages all of the mtproto stuff. */ -class MTProto implements TLCallback +class MTProto extends AsyncConstruct implements TLCallback { use \danog\Serializable; use \danog\MadelineProto\MTProtoTools\AckHandler; @@ -142,20 +143,18 @@ class MTProto implements TLCallback public $setdem = false; public $storage = []; private $postpone_updates = false; - private $altervista = false; private $supportUser = 0; public $referenceDatabase; public $update_deferred; public $phoneConfigWatcherId; - public $asyncInitPromise; public function __magic_construct($settings = []) { - $this->asyncInitPromise = $this->call($this->__async_construct($settings)); + $this->setInitPromise($this->__construct_async($settings)); } - public function __async_construct($settings = []) + public function __construct_async($settings = []) { \danog\MadelineProto\Magic::class_exists(); // Parse settings @@ -215,7 +214,6 @@ class MTProto implements TLCallback } yield $this->get_config_async([], ['datacenter' => $this->datacenter->curdc]); $this->v = self::V; - } public function __sleep() @@ -225,7 +223,7 @@ class MTProto implements TLCallback public function isAltervista() { - return $this->altervista; + return Magic::$altervista; } public function isInitingAuthorization() @@ -236,9 +234,9 @@ class MTProto implements TLCallback public function __wakeup() { $backtrace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT, 3); - $this->asyncInitPromise = $this->call($this->__async_wakeup($backtrace)); + $this->setInitPromise($this->__wakeup_async($backtrace)); } - public function __async_wakeup($backtrace) + public function __wakeup_async($backtrace) { set_error_handler(['\\danog\\MadelineProto\\Exception', 'ExceptionErrorHandler']); set_exception_handler(['\\danog\\MadelineProto\\Serialization', 'serialize_all']); @@ -267,7 +265,6 @@ class MTProto implements TLCallback $this->referenceDatabase = new ReferenceDatabase($this); } $this->update_callbacks([$this, $this->referenceDatabase]); - $this->altervista = isset($_SERVER['SERVER_ADMIN']) && strpos($_SERVER['SERVER_ADMIN'], 'altervista.org'); $this->settings['connection_settings']['all']['ipv6'] = \danog\MadelineProto\Magic::$ipv6; /*if (isset($this->settings['pwr']['update_handler']) && $this->settings['pwr']['update_handler'] === $this->settings['updates']['callback']) { @@ -374,7 +371,7 @@ class MTProto implements TLCallback $this->reset_session(true, true); $this->config = ['expires' => -1]; $this->dh_config = ['version' => 0]; - yield $this->__async_construct($settings); + yield $this->__construct_async($settings); $force = true; foreach ($this->secret_chats as $chat => $data) { try { @@ -548,7 +545,6 @@ class MTProto implements TLCallback $app_version = '4.9.1 (13613)'; } - $this->altervista = isset($_SERVER['SERVER_ADMIN']) && strpos($_SERVER['SERVER_ADMIN'], 'altervista.org'); // Set default settings $default_settings = ['authorization' => [ // Authorization settings @@ -615,7 +611,7 @@ class MTProto implements TLCallback // connection settings 'all' => [ // These settings will be applied on every datacenter that hasn't a custom settings subarray... - 'protocol' => $this->altervista ? 'http' : 'tcp_abridged', + 'protocol' => Magic::$altervista ? 'http' : 'tcp_abridged', // can be tcp_full, tcp_abridged, tcp_intermediate, http, https, obfuscated2, udp (unsupported) 'test_mode' => false, // decides whether to connect to the main telegram servers or to the testing servers (deep telegram) @@ -623,9 +619,9 @@ class MTProto implements TLCallback // decides whether to use ipv6, ipv6 attribute of API attribute of API class contains autodetected boolean 'timeout' => 2, // timeout for sockets - 'proxy' => $this->altervista ? '\\HttpProxy' : '\\Socket', + 'proxy' => Magic::$altervista ? '\\HttpProxy' : '\\Socket', // The proxy class to use - 'proxy_extra' => $this->altervista ? ['address' => 'localhost', 'port' => 80] : [], + 'proxy_extra' => Magic::$altervista ? ['address' => 'localhost', 'port' => 80] : [], // Extra parameters to pass to the proxy class using setExtra 'obfuscated' => false, 'transport' => 'tcp', diff --git a/src/danog/MadelineProto/MTProtoTools/PeerHandler.php b/src/danog/MadelineProto/MTProtoTools/PeerHandler.php index d38178bb..7213bf8f 100644 --- a/src/danog/MadelineProto/MTProtoTools/PeerHandler.php +++ b/src/danog/MadelineProto/MTProtoTools/PeerHandler.php @@ -790,15 +790,6 @@ trait PeerHandler { $settings = isset($this->settings['connection_settings'][$this->datacenter->curdc]) ? $this->settings['connection_settings'][$this->datacenter->curdc] : $this->settings['connection_settings']['all']; if (!isset($this->settings['pwr']) || $this->settings['pwr']['pwr'] === false || $settings['test_mode']) { - /* - try { - if (isset($res['username'])) { - shell_exec('curl '.escapeshellarg('https://api.pwrtelegram.xyz/getchat?chat_id=@'.$res['username']).' -s -o /dev/null >/dev/null 2>/dev/null & '); - } - } catch (\danog\MadelineProto\Exception $e) { - $this->logger->logger([$e->getMessage()); - } - */ return; } if (!empty($res)) { diff --git a/src/danog/MadelineProto/Magic.php b/src/danog/MadelineProto/Magic.php index de76c8d1..98ddae6d 100644 --- a/src/danog/MadelineProto/Magic.php +++ b/src/danog/MadelineProto/Magic.php @@ -51,6 +51,7 @@ class Magic public static $revision; public static $cwd; public static $script_cwd; + public static $altervista = false; const JSON_EMOJIS = '["\\ud83d\\ude09","\\ud83d\\ude0d","\\ud83d\\ude1b","\\ud83d\\ude2d","\\ud83d\\ude31","\\ud83d\\ude21","\\ud83d\\ude0e","\\ud83d\\ude34","\\ud83d\\ude35","\\ud83d\\ude08","\\ud83d\\ude2c","\\ud83d\\ude07","\\ud83d\\ude0f","\\ud83d\\udc6e","\\ud83d\\udc77","\\ud83d\\udc82","\\ud83d\\udc76","\\ud83d\\udc68","\\ud83d\\udc69","\\ud83d\\udc74","\\ud83d\\udc75","\\ud83d\\ude3b","\\ud83d\\ude3d","\\ud83d\\ude40","\\ud83d\\udc7a","\\ud83d\\ude48","\\ud83d\\ude49","\\ud83d\\ude4a","\\ud83d\\udc80","\\ud83d\\udc7d","\\ud83d\\udca9","\\ud83d\\udd25","\\ud83d\\udca5","\\ud83d\\udca4","\\ud83d\\udc42","\\ud83d\\udc40","\\ud83d\\udc43","\\ud83d\\udc45","\\ud83d\\udc44","\\ud83d\\udc4d","\\ud83d\\udc4e","\\ud83d\\udc4c","\\ud83d\\udc4a","\\u270c","\\u270b","\\ud83d\\udc50","\\ud83d\\udc46","\\ud83d\\udc47","\\ud83d\\udc49","\\ud83d\\udc48","\\ud83d\\ude4f","\\ud83d\\udc4f","\\ud83d\\udcaa","\\ud83d\\udeb6","\\ud83c\\udfc3","\\ud83d\\udc83","\\ud83d\\udc6b","\\ud83d\\udc6a","\\ud83d\\udc6c","\\ud83d\\udc6d","\\ud83d\\udc85","\\ud83c\\udfa9","\\ud83d\\udc51","\\ud83d\\udc52","\\ud83d\\udc5f","\\ud83d\\udc5e","\\ud83d\\udc60","\\ud83d\\udc55","\\ud83d\\udc57","\\ud83d\\udc56","\\ud83d\\udc59","\\ud83d\\udc5c","\\ud83d\\udc53","\\ud83c\\udf80","\\ud83d\\udc84","\\ud83d\\udc9b","\\ud83d\\udc99","\\ud83d\\udc9c","\\ud83d\\udc9a","\\ud83d\\udc8d","\\ud83d\\udc8e","\\ud83d\\udc36","\\ud83d\\udc3a","\\ud83d\\udc31","\\ud83d\\udc2d","\\ud83d\\udc39","\\ud83d\\udc30","\\ud83d\\udc38","\\ud83d\\udc2f","\\ud83d\\udc28","\\ud83d\\udc3b","\\ud83d\\udc37","\\ud83d\\udc2e","\\ud83d\\udc17","\\ud83d\\udc34","\\ud83d\\udc11","\\ud83d\\udc18","\\ud83d\\udc3c","\\ud83d\\udc27","\\ud83d\\udc25","\\ud83d\\udc14","\\ud83d\\udc0d","\\ud83d\\udc22","\\ud83d\\udc1b","\\ud83d\\udc1d","\\ud83d\\udc1c","\\ud83d\\udc1e","\\ud83d\\udc0c","\\ud83d\\udc19","\\ud83d\\udc1a","\\ud83d\\udc1f","\\ud83d\\udc2c","\\ud83d\\udc0b","\\ud83d\\udc10","\\ud83d\\udc0a","\\ud83d\\udc2b","\\ud83c\\udf40","\\ud83c\\udf39","\\ud83c\\udf3b","\\ud83c\\udf41","\\ud83c\\udf3e","\\ud83c\\udf44","\\ud83c\\udf35","\\ud83c\\udf34","\\ud83c\\udf33","\\ud83c\\udf1e","\\ud83c\\udf1a","\\ud83c\\udf19","\\ud83c\\udf0e","\\ud83c\\udf0b","\\u26a1","\\u2614","\\u2744","\\u26c4","\\ud83c\\udf00","\\ud83c\\udf08","\\ud83c\\udf0a","\\ud83c\\udf93","\\ud83c\\udf86","\\ud83c\\udf83","\\ud83d\\udc7b","\\ud83c\\udf85","\\ud83c\\udf84","\\ud83c\\udf81","\\ud83c\\udf88","\\ud83d\\udd2e","\\ud83c\\udfa5","\\ud83d\\udcf7","\\ud83d\\udcbf","\\ud83d\\udcbb","\\u260e","\\ud83d\\udce1","\\ud83d\\udcfa","\\ud83d\\udcfb","\\ud83d\\udd09","\\ud83d\\udd14","\\u23f3","\\u23f0","\\u231a","\\ud83d\\udd12","\\ud83d\\udd11","\\ud83d\\udd0e","\\ud83d\\udca1","\\ud83d\\udd26","\\ud83d\\udd0c","\\ud83d\\udd0b","\\ud83d\\udebf","\\ud83d\\udebd","\\ud83d\\udd27","\\ud83d\\udd28","\\ud83d\\udeaa","\\ud83d\\udeac","\\ud83d\\udca3","\\ud83d\\udd2b","\\ud83d\\udd2a","\\ud83d\\udc8a","\\ud83d\\udc89","\\ud83d\\udcb0","\\ud83d\\udcb5","\\ud83d\\udcb3","\\u2709","\\ud83d\\udceb","\\ud83d\\udce6","\\ud83d\\udcc5","\\ud83d\\udcc1","\\u2702","\\ud83d\\udccc","\\ud83d\\udcce","\\u2712","\\u270f","\\ud83d\\udcd0","\\ud83d\\udcda","\\ud83d\\udd2c","\\ud83d\\udd2d","\\ud83c\\udfa8","\\ud83c\\udfac","\\ud83c\\udfa4","\\ud83c\\udfa7","\\ud83c\\udfb5","\\ud83c\\udfb9","\\ud83c\\udfbb","\\ud83c\\udfba","\\ud83c\\udfb8","\\ud83d\\udc7e","\\ud83c\\udfae","\\ud83c\\udccf","\\ud83c\\udfb2","\\ud83c\\udfaf","\\ud83c\\udfc8","\\ud83c\\udfc0","\\u26bd","\\u26be","\\ud83c\\udfbe","\\ud83c\\udfb1","\\ud83c\\udfc9","\\ud83c\\udfb3","\\ud83c\\udfc1","\\ud83c\\udfc7","\\ud83c\\udfc6","\\ud83c\\udfca","\\ud83c\\udfc4","\\u2615","\\ud83c\\udf7c","\\ud83c\\udf7a","\\ud83c\\udf77","\\ud83c\\udf74","\\ud83c\\udf55","\\ud83c\\udf54","\\ud83c\\udf5f","\\ud83c\\udf57","\\ud83c\\udf71","\\ud83c\\udf5a","\\ud83c\\udf5c","\\ud83c\\udf61","\\ud83c\\udf73","\\ud83c\\udf5e","\\ud83c\\udf69","\\ud83c\\udf66","\\ud83c\\udf82","\\ud83c\\udf70","\\ud83c\\udf6a","\\ud83c\\udf6b","\\ud83c\\udf6d","\\ud83c\\udf6f","\\ud83c\\udf4e","\\ud83c\\udf4f","\\ud83c\\udf4a","\\ud83c\\udf4b","\\ud83c\\udf52","\\ud83c\\udf47","\\ud83c\\udf49","\\ud83c\\udf53","\\ud83c\\udf51","\\ud83c\\udf4c","\\ud83c\\udf50","\\ud83c\\udf4d","\\ud83c\\udf46","\\ud83c\\udf45","\\ud83c\\udf3d","\\ud83c\\udfe1","\\ud83c\\udfe5","\\ud83c\\udfe6","\\u26ea","\\ud83c\\udff0","\\u26fa","\\ud83c\\udfed","\\ud83d\\uddfb","\\ud83d\\uddfd","\\ud83c\\udfa0","\\ud83c\\udfa1","\\u26f2","\\ud83c\\udfa2","\\ud83d\\udea2","\\ud83d\\udea4","\\u2693","\\ud83d\\ude80","\\u2708","\\ud83d\\ude81","\\ud83d\\ude82","\\ud83d\\ude8b","\\ud83d\\ude8e","\\ud83d\\ude8c","\\ud83d\\ude99","\\ud83d\\ude97","\\ud83d\\ude95","\\ud83d\\ude9b","\\ud83d\\udea8","\\ud83d\\ude94","\\ud83d\\ude92","\\ud83d\\ude91","\\ud83d\\udeb2","\\ud83d\\udea0","\\ud83d\\ude9c","\\ud83d\\udea6","\\u26a0","\\ud83d\\udea7","\\u26fd","\\ud83c\\udfb0","\\ud83d\\uddff","\\ud83c\\udfaa","\\ud83c\\udfad","\\ud83c\\uddef\\ud83c\\uddf5","\\ud83c\\uddf0\\ud83c\\uddf7","\\ud83c\\udde9\\ud83c\\uddea","\\ud83c\\udde8\\ud83c\\uddf3","\\ud83c\\uddfa\\ud83c\\uddf8","\\ud83c\\uddeb\\ud83c\\uddf7","\\ud83c\\uddea\\ud83c\\uddf8","\\ud83c\\uddee\\ud83c\\uddf9","\\ud83c\\uddf7\\ud83c\\uddfa","\\ud83c\\uddec\\ud83c\\udde7","1\\u20e3","2\\u20e3","3\\u20e3","4\\u20e3","5\\u20e3","6\\u20e3","7\\u20e3","8\\u20e3","9\\u20e3","0\\u20e3","\\ud83d\\udd1f","\\u2757","\\u2753","\\u2665","\\u2666","\\ud83d\\udcaf","\\ud83d\\udd17","\\ud83d\\udd31","\\ud83d\\udd34","\\ud83d\\udd35","\\ud83d\\udd36","\\ud83d\\udd37"]'; @@ -87,6 +88,7 @@ class Magic } catch (\danog\MadelineProto\Exception $e) { } self::$can_getmypid = !(isset($_SERVER['SERVER_ADMIN']) && strpos($_SERVER['SERVER_ADMIN'], 'altervista.org')); + self::$altervista = !self::$can_getmypid; self::$revision = @file_get_contents(__DIR__.'/../../../.git/refs/heads/master'); if (self::$revision) { self::$revision = trim(self::$revision); diff --git a/src/danog/MadelineProto/MyTelegramOrgWrapper.php b/src/danog/MadelineProto/MyTelegramOrgWrapper.php index d014689f..a52d7ed3 100644 --- a/src/danog/MadelineProto/MyTelegramOrgWrapper.php +++ b/src/danog/MadelineProto/MyTelegramOrgWrapper.php @@ -18,6 +18,8 @@ namespace danog\MadelineProto; +use Amp\Artax\Request; + /** * Wrapper for my.telegram.org. */ @@ -25,119 +27,98 @@ class MyTelegramOrgWrapper { private $logged = false; private $hash = ''; + private $token; + private $number; + private $creation_hash; + private $settings; const MY_TELEGRAM_URL = 'https://my.telegram.org'; - public function __construct($number) + public function __sleep() { - if (!extension_loaded('curl')) { - throw new Exception(['extension', 'curl']); + return ['logged', 'hash', 'token', 'number', 'creation_hash', 'settings']; + } + public function __construct($settings) + { + if (!isset($settings['all'])) { + $settings['connection_settings'] = ['all' => [ + // These settings will be applied on every datacenter that hasn't a custom settings subarray... + 'protocol' => Magic::$altervista ? 'http' : 'tcp_abridged', + // can be tcp_full, tcp_abridged, tcp_intermediate, http, https, obfuscated2, udp (unsupported) + 'test_mode' => false, + // decides whether to connect to the main telegram servers or to the testing servers (deep telegram) + 'ipv6' => \danog\MadelineProto\Magic::$ipv6, + // decides whether to use ipv6, ipv6 attribute of API attribute of API class contains autodetected boolean + 'timeout' => 2, + // timeout for sockets + 'proxy' => Magic::$altervista ? '\\HttpProxy' : '\\Socket', + // The proxy class to use + 'proxy_extra' => Magic::$altervista ? ['address' => 'localhost', 'port' => 80] : [], + // Extra parameters to pass to the proxy class using setExtra + 'obfuscated' => false, + 'transport' => 'tcp', + 'pfs' => extension_loaded('gmp'), + ], + ]; } + $this->settings = $settings; + $this->__wakeup(); + } + public function __wakeup() + { + $this->datacenter = new DataCenter( + new class($this->settings) + { + public function __construct($settings) + { + $this->logger = new Logger( + isset($settings['logger']['logger']) ? $settings['logger']['logger'] : php_sapi_name() === 'cli' ? 3 : 2, + isset($settings['logger']['logger_param']) ? $settings['logger']['logger_param'] : Magic::$script_cwd.'/MadelineProto.log', + isset($settings['logger']['logger_level']) ? $settings['logger']['logger_level'] : Logger::VERBOSE, + isset($settings['logger']['max_size']) ? $settings['logger']['max_size'] : 100 * 1024 * 1024); + } + }, + [], + $this->settings['connection_settings'] + ); + } + public function login_async($number) + { $this->number = $number; - $ch = curl_init(); - - curl_setopt($ch, CURLOPT_URL, self::MY_TELEGRAM_URL.'/auth/send_password'); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); - curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query(['phone' => $number])); - curl_setopt($ch, CURLOPT_POST, 1); - curl_setopt($ch, CURLOPT_ENCODING, 'gzip, deflate'); - - $headers = $this->get_headers('origin', []); - curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); - - $result = curl_exec($ch); - if (curl_errno($ch)) { - throw new Exception('Curl error: '.curl_error($ch)); - } - curl_close($ch); + $request = new Request(self::MY_TELEGRAM_URL.'/auth/send_password', 'POST'); + $request = $request->withBody(http_build_query(['phone' => $number])); + $request = $request->withHeaders($this->getHeaders('origin')); + $response = yield $this->datacenter->getHTTPClient()->request($request); + $result = yield $response->getBody(); $resulta = json_decode($result, true); + if (!isset($resulta['random_hash'])) { throw new Exception($result); } $this->hash = $resulta['random_hash']; } - /** - * Function for generating curl request headers. - */ - private function get_headers($httpType, $cookies) - { - // Common header flags. - $headers = []; - $headers[] = 'Dnt: 1'; - $headers[] = 'Connection: keep-alive'; - $headers[] = 'Accept-Language: it-IT,it;q=0.8,en-US;q=0.6,en;q=0.4'; - $headers[] = 'User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36'; - - // Add additional headers based on the type of request. - switch ($httpType) { - case 'origin': - $headers[] = 'Origin: '.self::MY_TELEGRAM_URL; - $headers[] = 'Accept-Encoding: gzip, deflate, br'; - $headers[] = 'Content-Type: application/x-www-form-urlencoded; charset=UTF-8'; - $headers[] = 'Accept: application/json, text/javascript, */*; q=0.01'; - $headers[] = 'Referer: '.self::MY_TELEGRAM_URL.'/auth'; - $headers[] = 'X-Requested-With: XMLHttpRequest'; - break; - case 'refer': - $headers[] = 'Accept-Encoding: gzip, deflate, sdch, br'; - $headers[] = 'Upgrade-Insecure-Requests: 1'; - $headers[] = 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8'; - $headers[] = 'Referer: '.self::MY_TELEGRAM_URL; - $headers[] = 'Cache-Control: max-age=0'; - break; - case 'app': - $headers[] = 'Origin: '.self::MY_TELEGRAM_URL; - $headers[] = 'Accept-Encoding: gzip, deflate, br'; - $headers[] = 'Content-Type: application/x-www-form-urlencoded; charset=UTF-8'; - $headers[] = 'Accept: */*'; - $headers[] = 'Referer: '.self::MY_TELEGRAM_URL.'/apps'; - $headers[] = 'X-Requested-With: XMLHttpRequest'; - break; - } - - // Add every cookie to the header. - foreach ($cookies as $cookie) { - $headers[] = 'Cookie: '.$cookie; - } - - return $headers; - } - - public function complete_login($password) + public function complete_login_async($password) { if ($this->logged) { throw new Exception('Already logged in!'); } - $ch = curl_init(); - curl_setopt($ch, CURLOPT_URL, self::MY_TELEGRAM_URL.'/auth/login'); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); - curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query(['phone' => $this->number, 'random_hash' => $this->hash, 'password' => $password])); - curl_setopt($ch, CURLOPT_POST, 1); - curl_setopt($ch, CURLOPT_ENCODING, 'gzip, deflate'); + $request = new Request(self::MY_TELEGRAM_URL.'/auth/login', 'POST'); + $request = $request->withBody(http_build_query(['phone' => $this->number, 'random_hash' => $this->hash, 'password' => $password])); + $request = $request->withHeaders($this->getHeaders('origin')); + $request = $request->withHeader('user-agent', 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.13) Gecko/20080311 Firefox/2.0.0.13'); + $response = yield $this->datacenter->getHTTPClient()->request($request); + $result = yield $response->getBody(); - $headers = $this->get_headers('origin', []); - - curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); - curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); - curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.13) Gecko/20080311 Firefox/2.0.0.13'); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); - curl_setopt($ch, CURLOPT_HEADER, 1); - $result = curl_exec($ch); - if (curl_errno($ch)) { - throw new Exception('Curl error: '.curl_error($ch)); - } - curl_close($ch); - - list($response_headers, $response_content) = preg_split('/(\r\n){2}/', $result, 2); - switch ($response_content) { + switch ($result) { case 'true': //Logger::log(['Login OK'], Logger::VERBOSE); break; default: - throw new Exception($response_content); + throw new Exception($result); } - $this->token = explode(';', explode('stel_token=', $response_headers)[1])[0]; + + $this->token = explode(';', explode('stel_token=', $response->getHeader('Set-Cookie'))[1])[0]; return $this->logged = true; } @@ -147,27 +128,17 @@ class MyTelegramOrgWrapper return $this->logged; } - public function has_app() + public function has_app_async() { if (!$this->logged) { throw new Exception('Not logged in!'); } - $ch = curl_init(); - curl_setopt($ch, CURLOPT_URL, self::MY_TELEGRAM_URL.'/apps'); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); - curl_setopt($ch, CURLOPT_ENCODING, 'gzip, deflate'); + $request = new Request(self::MY_TELEGRAM_URL.'/apps'); + $request = $request->withHeaders($this->getHeaders('refer')); + $response = yield $this->datacenter->getHTTPClient()->request($request); + $result = yield $response->getBody(); - $cookies = []; - array_push($cookies, 'stel_token='.$this->token); - $headers = $this->get_headers('refer', $cookies); - - curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); - $result = curl_exec($ch); - if (curl_errno($ch)) { - throw new Exception('Curl error: '.curl_error($ch)); - } - curl_close($ch); $title = explode('', explode('', $result)[1])[0]; switch ($title) { case 'App configuration':return true; @@ -180,27 +151,16 @@ class MyTelegramOrgWrapper throw new Exception($title); } - public function get_app() + public function get_app_async() { if (!$this->logged) { throw new Exception('Not logged in!'); } - $ch = curl_init(); - curl_setopt($ch, CURLOPT_URL, self::MY_TELEGRAM_URL.'/apps'); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); - curl_setopt($ch, CURLOPT_ENCODING, 'gzip, deflate'); - - $cookies = []; - array_push($cookies, 'stel_token='.$this->token); - $headers = $this->get_headers('refer', $cookies); - - curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); - $result = curl_exec($ch); - if (curl_errno($ch)) { - throw new Exception('Curl error: '.curl_error($ch)); - } - curl_close($ch); + $request = new Request(self::MY_TELEGRAM_URL.'/apps'); + $request = $request->withHeaders($this->getHeaders('refer')); + $response = yield $this->datacenter->getHTTPClient()->request($request); + $result = yield $response->getBody(); $cose = explode('<label for="app_id" class="col-md-4 text-right control-label">App api_id:</label> <div class="col-md-7"> @@ -216,58 +176,29 @@ class MyTelegramOrgWrapper return ['api_id' => (int) $api_id, 'api_hash' => $api_hash]; } - public function create_app($settings) + public function create_app_async($settings) { if (!$this->logged) { throw new Exception('Not logged in!'); } - if ($this->has_app()) { + if (yield $this->has_app_async()) { throw new Exception('The app was already created!'); } - $ch = curl_init(); - - curl_setopt($ch, CURLOPT_URL, self::MY_TELEGRAM_URL.'/apps/create'); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); - curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query(['hash' => $this->creation_hash, 'app_title' => $settings['app_title'], 'app_shortname' => $settings['app_shortname'], 'app_url' => $settings['app_url'], 'app_platform' => $settings['app_platform'], 'app_desc' => $settings['app_desc']])); - curl_setopt($ch, CURLOPT_POST, 1); - curl_setopt($ch, CURLOPT_ENCODING, 'gzip, deflate'); - - $cookies = []; - array_push($cookies, 'stel_token='.$this->token); - $headers = $this->get_headers('app', $cookies); - - curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); - - $result = curl_exec($ch); - if (curl_errno($ch)) { - throw new Exception('Curl error:'.curl_error($ch)); - } - curl_close($ch); + $request = new Request(self::MY_TELEGRAM_URL.'/apps/create', 'POST'); + $request = $request->withHeaders($this->getHeaders('app')); + $request = $request->withBody(http_build_query(['hash' => $this->creation_hash, 'app_title' => $settings['app_title'], 'app_shortname' => $settings['app_shortname'], 'app_url' => $settings['app_url'], 'app_platform' => $settings['app_platform'], 'app_desc' => $settings['app_desc']])); + $response = yield $this->datacenter->getHTTPClient()->request($request); + $result = yield $response->getBody(); if ($result) { throw new Exception($result); } - $ch = curl_init(); - - curl_setopt($ch, CURLOPT_URL, self::MY_TELEGRAM_URL.'/apps'); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); - curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET'); - - curl_setopt($ch, CURLOPT_ENCODING, 'gzip, deflate'); - - $cookies = []; - array_push($cookies, 'stel_token='.$this->token); - $headers = $this->get_headers('refer', $cookies); - - curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); - - $result = curl_exec($ch); - if (curl_errno($ch)) { - throw new Exception('Curl error:'.curl_error($ch)); - } - curl_close($ch); + $request = new Request(self::MY_TELEGRAM_URL.'/apps'); + $request = $request->withHeaders($this->getHeaders('refer')); + $response = yield $this->datacenter->getHTTPClient()->request($request); + $result = yield $response->getBody(); $title = explode('', explode('', $result)[1])[0]; if ($title === 'Create new application') { @@ -289,4 +220,53 @@ class MyTelegramOrgWrapper return ['api_id' => (int) $api_id, 'api_hash' => $api_hash]; } + + /** + * Function for generating curl request headers. + */ + private function getHeaders($httpType) + { + // Common header flags. + $headers = []; + $headers[] = 'Dnt: 1'; + $headers[] = 'Connection: keep-alive'; + $headers[] = 'Accept-Language: it-IT,it;q=0.8,en-US;q=0.6,en;q=0.4'; + $headers[] = 'User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36'; + + // Add additional headers based on the type of request. + switch ($httpType) { + case 'origin': + $headers[] = 'Origin: '.self::MY_TELEGRAM_URL; + //$headers[] = 'Accept-Encoding: gzip, deflate, br'; + $headers[] = 'Content-Type: application/x-www-form-urlencoded; charset=UTF-8'; + $headers[] = 'Accept: application/json, text/javascript, */*; q=0.01'; + $headers[] = 'Referer: '.self::MY_TELEGRAM_URL.'/auth'; + $headers[] = 'X-Requested-With: XMLHttpRequest'; + break; + case 'refer': + //$headers[] = 'Accept-Encoding: gzip, deflate, sdch, br'; + $headers[] = 'Upgrade-Insecure-Requests: 1'; + $headers[] = 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8'; + $headers[] = 'Referer: '.self::MY_TELEGRAM_URL; + $headers[] = 'Cache-Control: max-age=0'; + break; + case 'app': + $headers[] = 'Origin: '.self::MY_TELEGRAM_URL; + //$headers[] = 'Accept-Encoding: gzip, deflate, br'; + $headers[] = 'Content-Type: application/x-www-form-urlencoded; charset=UTF-8'; + $headers[] = 'Accept: */*'; + $headers[] = 'Referer: '.self::MY_TELEGRAM_URL.'/apps'; + $headers[] = 'X-Requested-With: XMLHttpRequest'; + break; + } + + $final_headers = []; + foreach ($headers as $header) { + list($key, $value) = explode(':', $header, 2); + $final_headers[trim($key)] = trim($value); + } + + return $final_headers; + } + } diff --git a/src/danog/MadelineProto/Serialization.php b/src/danog/MadelineProto/Serialization.php index dd805626..8f44e2ca 100644 --- a/src/danog/MadelineProto/Serialization.php +++ b/src/danog/MadelineProto/Serialization.php @@ -29,11 +29,6 @@ class Serialization echo $exception.PHP_EOL; return; - foreach (self::$instances as $instance) { - if (isset($instance->session)) { - $instance->serialize(); - } - } } public static function realpaths($file) @@ -42,68 +37,4 @@ class Serialization return ['file' => $file, 'lockfile' => $file.'.lock', 'tempfile' => $file.'.temp.session']; } - - /** - * Serialize API class. - * - * @param string $filename the dump file - * @param API $instance - * @param bool $force - * - * @return number - */ - public static function serialize($filename, $instance, $force = false) - { - if ($filename == '') { - throw new \danog\MadelineProto\Exception('Empty filename'); - } - if (isset($instance->API->setdem) && $instance->API->setdem) { - $instance->API->setdem = false; - $instance->API->__construct($instance->API->settings); - } - if ($instance->API === null && !$instance->getting_api_id) { - return false; - } - if ($instance->API && $instance->API->asyncInitPromise) { - return $instance->call((static function () use ($filename, $instance, $force) { - yield $instance->API->asyncInitPromise; - $instance->API->asyncInitPromise = null; - return self::serialize($filename, $instance, $force); - })()); - } - $instance->serialized = time(); - $realpaths = self::realpaths($filename); - if (!file_exists($realpaths['lockfile'])) { - touch($realpaths['lockfile']); - clearstatcache(); - } - $realpaths['lockfile'] = fopen($realpaths['lockfile'], 'w'); - \danog\MadelineProto\Logger::log('Waiting for exclusive lock of serialization lockfile...'); - flock($realpaths['lockfile'], LOCK_EX); - \danog\MadelineProto\Logger::log('Lock acquired, serializing'); - - try { - if (!$instance->getting_api_id) { - $update_closure = $instance->API->settings['updates']['callback']; - if ($instance->API->settings['updates']['callback'] instanceof \Closure) { - $instance->API->settings['updates']['callback'] = [$instance->API, 'noop']; - } - $logger_closure = $instance->API->settings['logger']['logger_param']; - if ($instance->API->settings['logger']['logger_param'] instanceof \Closure) { - $instance->API->settings['logger']['logger_param'] = [$instance->API, 'noop']; - } - } - $wrote = file_put_contents($realpaths['tempfile'], serialize($instance)); - rename($realpaths['tempfile'], $realpaths['file']); - } finally { - if (!$instance->getting_api_id) { - $instance->API->settings['updates']['callback'] = $update_closure; - $instance->API->settings['logger']['logger_param'] = $logger_closure; - } - flock($realpaths['lockfile'], LOCK_UN); - fclose($realpaths['lockfile']); - } - - return $wrote; - } } diff --git a/src/danog/MadelineProto/Stream/Common/BufferedRawStream.php b/src/danog/MadelineProto/Stream/Common/BufferedRawStream.php index 0e3bd604..ceb18243 100644 --- a/src/danog/MadelineProto/Stream/Common/BufferedRawStream.php +++ b/src/danog/MadelineProto/Stream/Common/BufferedRawStream.php @@ -42,7 +42,6 @@ class BufferedRawStream implements \danog\MadelineProto\Stream\BufferedStreamInt protected $memory_stream; private $append = ''; private $append_after = 0; - /** * Asynchronously connect to a TCP/TLS server. * @@ -178,7 +177,6 @@ class BufferedRawStream implements \danog\MadelineProto\Stream\BufferedStreamInt $chunk = yield $this->read(); if ($chunk === null) { $this->disconnect(); - throw new \danog\MadelineProto\NothingInTheSocketException(); } fwrite($this->memory_stream, $chunk); diff --git a/src/danog/MadelineProto/Tools.php b/src/danog/MadelineProto/Tools.php index 812b758b..35e8fa3f 100644 --- a/src/danog/MadelineProto/Tools.php +++ b/src/danog/MadelineProto/Tools.php @@ -252,6 +252,7 @@ trait Tools } }); } + return $promise; } public function rethrow($e) { diff --git a/src/danog/MadelineProto/Wrappers/ApiStart.php b/src/danog/MadelineProto/Wrappers/ApiStart.php index c54127f4..1c0f34b4 100644 --- a/src/danog/MadelineProto/Wrappers/ApiStart.php +++ b/src/danog/MadelineProto/Wrappers/ApiStart.php @@ -19,30 +19,34 @@ namespace danog\MadelineProto\Wrappers; +use function Amp\ByteStream\getStdin; +use function Amp\ByteStream\getStdout; + /** * Manages simple logging in and out. */ trait ApiStart { - public function api_start() + public function api_start_async($settings) { if (php_sapi_name() === 'cli') { - if (!function_exists('readline')) { - $readline = function ($prompt = null) { - if ($prompt) { - echo $prompt; - } - $fp = fopen('php://stdin', 'r'); - $line = rtrim(fgets($fp, 1024)); - - return $line; - }; - } else { - $readline = 'readline'; - } + $stdin = getStdin(); + $stdout = getStdout(); + $readline = function ($prompt = null) use ($stdout, $stdin) { + if ($prompt) { + yield $stdout->write($prompt); + } + static $lines = ['']; + while (count($lines) < 2 && ($chunk = yield $stdin->read()) !== null) { + $chunk = explode("\n", str_replace(["\r", "\n\n"], "\n", $chunk)); + $lines[count($lines) - 1] .= array_shift($chunk); + $lines = array_merge($lines, $chunk); + } + return array_shift($lines); + }; echo 'You did not define a valid API ID/API hash. Do you want to define it now manually, or automatically? (m/a) Note that you can also provide the API parameters directly in the code using the settings: https://docs.madelineproto.xyz/docs/SETTINGS.html#settingsapp_infoapi_id'.PHP_EOL; - if (strpos($res = $readline('Your choice (m/a): '), 'm') !== false) { + if (strpos(yield $readline('Your choice (m/a): '), 'm') !== false) { echo '1) Login to my.telegram.org 2) Go to API development tools 3) App title: your app\'s name, can be anything @@ -51,21 +55,22 @@ Note that you can also provide the API parameters directly in the code using the Platform: anything Description: Describe your app here 4) Click on create application'.PHP_EOL; - $app['api_id'] = $readline('5) Enter your API ID: '); - $app['api_hash'] = $readline('6) Enter your API hash: '); + $app['api_id'] = yield $readline('5) Enter your API ID: '); + $app['api_hash'] = yield $readline('6) Enter your API hash: '); return $app; } else { - $this->my_telegram_org_wrapper = new \danog\MadelineProto\MyTelegramOrgWrapper($readline('Enter a phone number that is already registered on Telegram: ')); - $this->my_telegram_org_wrapper->complete_login($readline('Enter the verification code you received in telegram: ')); - if (!$this->my_telegram_org_wrapper->has_app()) { - $app_title = $readline('Enter the app\'s name, can be anything: '); - $short_name = $readline('Enter the app\'s short name, can be anything: '); - $url = $readline('Enter the app/website\'s URL, or t.me/yourusername: '); - $description = $readline('Describe your app: '); - $app = $this->my_telegram_org_wrapper->create_app(['app_title' => $app_title, 'app_shortname' => $short_name, 'app_url' => $short_name, 'app_platform' => 'web', 'app_desc' => $description]); + $this->my_telegram_org_wrapper = new \danog\MadelineProto\MyTelegramOrgWrapper($settings); + yield $this->my_telegram_org_wrapper->login_async(yield $readline('Enter a phone number that is already registered on Telegram: ')); + yield $this->my_telegram_org_wrapper->complete_login_async(yield $readline('Enter the verification code you received in telegram: ')); + if (!yield $this->my_telegram_org_wrapper->has_app_async()) { + $app_title = yield $readline('Enter the app\'s name, can be anything: '); + $short_name = yield $readline('Enter the app\'s short name, can be anything: '); + $url = yield $readline('Enter the app/website\'s URL, or t.me/yourusername: '); + $description = yield $readline('Describe your app: '); + $app = yield $this->my_telegram_org_wrapper->create_app_async(['app_title' => $app_title, 'app_shortname' => $short_name, 'app_url' => $url, 'app_platform' => 'web', 'app_desc' => $description]); } else { - $app = $this->my_telegram_org_wrapper->get_app(); + $app = yield $this->my_telegram_org_wrapper->get_app_async(); } return $app; @@ -80,69 +85,78 @@ Note that you can also provide the API parameters directly in the code using the return $app; } elseif (isset($_POST['phone_number'])) { - $this->web_api_phone_login(); + yield $this->web_api_phone_login_async($settings); } else { - $this->web_api_echo(); + yield $this->web_api_echo_async(); } } elseif (!$this->my_telegram_org_wrapper->logged_in()) { if (isset($_POST['code'])) { - $this->web_api_complete_login(); - if ($this->my_telegram_org_wrapper->has_app()) { - return $this->my_telegram_org_wrapper->get_app(); + yield $this->web_api_complete_login_async(); + if (yield $this->my_telegram_org_wrapper->has_app_async()) { + return yield $this->my_telegram_org_wrapper->get_app_async(); } - $this->web_api_echo(); + yield $this->web_api_echo_async(); + } else if (isset($_POST['api_id']) && isset($_POST['api_hash'])) { + $app['api_id'] = (int) $_POST['api_id']; + $app['api_hash'] = $_POST['api_hash']; + $this->getting_api_id = false; + + return $app; + } elseif (isset($_POST['phone_number'])) { + yield $this->web_api_phone_login_async($settings); } else { - $this->web_api_echo("You didn't provide a phone code!"); + $this->my_telegram_org_wrapper = null; + yield $this->web_api_echo_async(); } } else { if (isset($_POST['app_title'], $_POST['app_shortname'], $_POST['app_url'], $_POST['app_platform'], $_POST['app_desc'])) { - $app = $this->web_api_create_app(); + $app = yield $this->web_api_create_app_async(); $this->getting_api_id = false; return $app; } else { - $this->web_api_echo("You didn't provide all of the required parameters!"); + yield $this->web_api_echo_async("You didn't provide all of the required parameters!"); } } + $this->asyncInitPromise = null; exit; } } - public function web_api_phone_login() + public function web_api_phone_login_async($settings) { try { - $this->my_telegram_org_wrapper = new \danog\MadelineProto\MyTelegramOrgWrapper($_POST['phone_number']); - $this->web_api_echo(); - } catch (\danog\MadelineProto\RPCErrorException $e) { - $this->web_api_echo('ERROR: '.$e->getMessage().'. Try again.'); - } catch (\danog\MadelineProto\Exception $e) { - $this->web_api_echo('ERROR: '.$e->getMessage().'. Try again.'); + $this->my_telegram_org_wrapper = new \danog\MadelineProto\MyTelegramOrgWrapper($settings); + yield $this->my_telegram_org_wrapper->login_async($_POST['phone_number']); + yield $this->web_api_echo_async(); + } catch (\Throwable $e) { + yield $this->web_api_echo_async('ERROR: '.$e->getMessage().'. Try again.'); } } - public function web_api_complete_login() + public function web_api_complete_login_async() { try { - $this->my_telegram_org_wrapper->complete_login($_POST['code']); + yield $this->my_telegram_org_wrapper->complete_login_async($_POST['code']); } catch (\danog\MadelineProto\RPCErrorException $e) { - $this->web_api_echo('ERROR: '.$e->getMessage().'. Try again.'); + yield $this->web_api_echo_async('ERROR: '.$e->getMessage().'. Try again.'); } catch (\danog\MadelineProto\Exception $e) { - $this->web_api_echo('ERROR: '.$e->getMessage().'. Try again.'); + yield $this->web_api_echo_async('ERROR: '.$e->getMessage().'. Try again.'); } } - public function web_api_create_app() + public function web_api_create_app_async() { try { $params = $_POST; unset($params['creating_app']); - $app = $this->my_telegram_org_wrapper->create_app($params); + $app = yield $this->my_telegram_org_wrapper->create_app_async($params); return $app; } catch (\danog\MadelineProto\RPCErrorException $e) { - $this->web_api_echo('ERROR: '.$e->getMessage().' Try again.'); + yield $this->web_api_echo_async('ERROR: '.$e->getMessage().' Try again.'); } catch (\danog\MadelineProto\Exception $e) { - $this->web_api_echo('ERROR: '.$e->getMessage().' Try again.'); + yield $this->web_api_echo_async('ERROR: '.$e->getMessage().' Try again.'); } } } diff --git a/src/danog/MadelineProto/Wrappers/ApiTemplates.php b/src/danog/MadelineProto/Wrappers/ApiTemplates.php index 53908052..b4e74460 100644 --- a/src/danog/MadelineProto/Wrappers/ApiTemplates.php +++ b/src/danog/MadelineProto/Wrappers/ApiTemplates.php @@ -19,6 +19,8 @@ namespace danog\MadelineProto\Wrappers; +use function Amp\ByteStream\getOutput; + trait ApiTemplates { private $web_api_template = '<!DOCTYPE html> @@ -51,12 +53,13 @@ trait ApiTemplates $this->web_template = $template; } - public function web_api_echo($message = '') + public function web_api_echo_async($message = '') { + $stdout = getOutput(); if (!isset($this->my_telegram_org_wrapper)) { if (isset($_POST['type'])) { if ($_POST['type'] === 'manual') { - echo $this->web_api_echo_template('Enter your API ID and API hash<br><b>'.$message.'</b><ol> + yield $stdout->write($this->web_api_echo_template('Enter your API ID and API hash<br><b>'.$message.'</b><ol> <li>Login to my.telegram.org</li> <li>Go to API development tools</li> <li> @@ -68,18 +71,21 @@ trait ApiTemplates </ul> </li> <li>Click on create application</li> -</ol>', '<input type="string" name="api_id" placeholder="API ID" required/><input type="string" name="api_hash" placeholder="API hash" required/>'); +</ol>', '<input type="string" name="api_id" placeholder="API ID" required/><input type="string" name="api_hash" placeholder="API hash" required/>')); } else { - echo $this->web_api_echo_template('Enter your phone number<br><b>'.$message.'</b>', '<input type="text" name="phone_number" placeholder="Phone number" required/>'); + yield $stdout->write($this->web_api_echo_template('Enter your phone number<br><b>'.$message.'</b>', '<input type="text" name="phone_number" placeholder="Phone number" required/>')); } } else { - echo $this->web_api_echo_template('Do you want to enter the API id and the API hash manually or automatically?<br>Note that you can also provide it directly in the code using the <a href="https://docs.madelineproto.xyz/docs/SETTINGS.html#settingsapp_infoapi_id">settings</a>.<b>'.$message.'</b>', '<select name="type"><option value="automatic">Automatically</option><option value="manual">Manually</option></select>'); + if ($message) { + $message = '<br><br>'.$message; + } + yield $stdout->write($this->web_api_echo_template('Do you want to enter the API id and the API hash manually or automatically?<br>Note that you can also provide it directly in the code using the <a href="https://docs.madelineproto.xyz/docs/SETTINGS.html#settingsapp_infoapi_id">settings</a>.<b>'.$message.'</b>', '<select name="type"><option value="automatic">Automatically</option><option value="manual">Manually</option></select>')); } } else { if (!$this->my_telegram_org_wrapper->logged_in()) { - echo $this->web_api_echo_template('Enter your code<br><b>'.$message.'</b>', '<input type="text" name="code" placeholder="Code" required/>'); + yield $stdout->write($this->web_api_echo_template('Enter your code<br><b>'.$message.'</b>', '<input type="text" name="code" placeholder="Code" required/>')); } else { - echo $this->web_api_echo_template( + yield $stdout->write($this->web_api_echo_template( 'Enter the API info<br><b>'.$message.'</b>', '<input type="hidden" name="creating_app" value="yes" required/> Enter the app name, can be anything: <br><input type="text" name="app_title" required/><br> @@ -111,7 +117,7 @@ trait ApiTemplates <input type="radio" name="app_platform" value="other"> Other (specify in description) </label> <br><br>Enter the app description, can be anything: <br><textarea name="app_desc" required></textarea><br><br> - '); + ')); } } } diff --git a/src/danog/MadelineProto/Wrappers/Start.php b/src/danog/MadelineProto/Wrappers/Start.php index 79eeb0c4..bdbbef40 100644 --- a/src/danog/MadelineProto/Wrappers/Start.php +++ b/src/danog/MadelineProto/Wrappers/Start.php @@ -19,6 +19,9 @@ namespace danog\MadelineProto\Wrappers; +use function Amp\ByteStream\getStdin; +use function Amp\ByteStream\getStdout; + /** * Manages simple logging in and out. */ @@ -30,29 +33,30 @@ trait Start return yield $this->get_self_async(); } if (php_sapi_name() === 'cli') { - if (!function_exists('readline')) { - $readline = function ($prompt = null) { - if ($prompt) { - echo $prompt; - } - $fp = fopen('php://stdin', 'r'); - $line = rtrim(fgets($fp, 1024)); - - return $line; - }; + $stdin = getStdin(); + $stdout = getStdout(); + $readline = function ($prompt = null) use ($stdout, $stdin) { + if ($prompt) { + yield $stdout->write($prompt); + } + static $lines = ['']; + while (count($lines) < 2 && ($chunk = yield $stdin->read()) !== null) { + $chunk = explode("\n", str_replace(["\r", "\n\n"], "\n", $chunk)); + $lines[count($lines) - 1] .= array_shift($chunk); + $lines = array_merge($lines, $chunk); + } + return array_shift($lines); + }; + if (strpos(yield $readline('Do you want to login as user or bot (u/b)? '), 'b') !== false) { + yield $this->bot_login_async(yield $readline('Enter your bot token: ')); } else { - $readline = 'readline'; - } - if (strpos($readline('Do you want to login as user or bot (u/b)? '), 'b') !== false) { - yield $this->bot_login_async($readline('Enter your bot token: ')); - } else { - yield $this->phone_login_async($readline('Enter your phone number: ')); - $authorization = yield $this->complete_phone_login_async($readline('Enter the phone code: ')); + yield $this->phone_login_async(yield $readline('Enter your phone number: ')); + $authorization = yield $this->complete_phone_login_async(yield $readline('Enter the phone code: ')); if ($authorization['_'] === 'account.password') { - $authorization = yield $this->complete_2fa_login_async($readline('Please enter your password (hint '.$authorization['hint'].'): ')); + $authorization = yield $this->complete_2fa_login_async(yield $readline('Please enter your password (hint '.$authorization['hint'].'): ')); } if ($authorization['_'] === 'account.needSignup') { - $authorization = yield $this->complete_signup_async($readline('Please enter your first name: '), $readline('Please enter your last name (can be empty): ')); + $authorization = yield $this->complete_signup_async(yield $readline('Please enter your first name: '), yield $readline('Please enter your last name (can be empty): ')); } } $this->serialize(); @@ -65,25 +69,25 @@ trait Start } elseif (isset($_POST['token'])) { yield $this->web_bot_login_async(); } else { - $this->web_echo(); + yield $this->web_echo_async(); } } elseif ($this->authorized === self::WAITING_CODE) { if (isset($_POST['phone_code'])) { yield $this->web_complete_phone_login_async(); } else { - $this->web_echo("You didn't provide a phone code!"); + yield $this->web_echo_async("You didn't provide a phone code!"); } } elseif ($this->authorized === self::WAITING_PASSWORD) { if (isset($_POST['password'])) { yield $this->web_complete_2fa_login_async(); } else { - $this->web_echo("You didn't provide the password!"); + yield $this->web_echo_async("You didn't provide the password!"); } } elseif ($this->authorized === self::WAITING_SIGNUP) { if (isset($_POST['first_name'])) { yield $this->web_complete_signup_async(); } else { - $this->web_echo("You didn't provide the first name!"); + yield $this->web_echo_async("You didn't provide the first name!"); } } if ($this->authorized === self::LOGGED_IN) { @@ -99,11 +103,11 @@ trait Start { try { yield $this->phone_login_async($_POST['phone_number']); - $this->web_echo(); + yield $this->web_echo_async(); } catch (\danog\MadelineProto\RPCErrorException $e) { - $this->web_echo('ERROR: '.$e->getMessage().'. Try again.'); + yield $this->web_echo_async('ERROR: '.$e->getMessage().'. Try again.'); } catch (\danog\MadelineProto\Exception $e) { - $this->web_echo('ERROR: '.$e->getMessage().'. Try again.'); + yield $this->web_echo_async('ERROR: '.$e->getMessage().'. Try again.'); } } @@ -111,11 +115,11 @@ trait Start { try { yield $this->complete_phone_login_async($_POST['phone_code']); - $this->web_echo(); + yield $this->web_echo_async(); } catch (\danog\MadelineProto\RPCErrorException $e) { - $this->web_echo('ERROR: '.$e->getMessage().'. Try again.'); + yield $this->web_echo_async('ERROR: '.$e->getMessage().'. Try again.'); } catch (\danog\MadelineProto\Exception $e) { - $this->web_echo('ERROR: '.$e->getMessage().'. Try again.'); + yield $this->web_echo_async('ERROR: '.$e->getMessage().'. Try again.'); } } @@ -123,11 +127,11 @@ trait Start { try { yield $this->complete_2fa_login_async($_POST['password']); - $this->web_echo(); + yield $this->web_echo_async(); } catch (\danog\MadelineProto\RPCErrorException $e) { - $this->web_echo('ERROR: '.$e->getMessage().'. Try again.'); + yield $this->web_echo_async('ERROR: '.$e->getMessage().'. Try again.'); } catch (\danog\MadelineProto\Exception $e) { - $this->web_echo('ERROR: '.$e->getMessage().'. Try again.'); + yield $this->web_echo_async('ERROR: '.$e->getMessage().'. Try again.'); } } @@ -135,11 +139,11 @@ trait Start { try { yield $this->complete_signup_async($_POST['first_name'], isset($_POST['last_name']) ? $_POST['last_name'] : ''); - $this->web_echo(); + yield $this->web_echo_async(); } catch (\danog\MadelineProto\RPCErrorException $e) { - $this->web_echo('ERROR: '.$e->getMessage().'. Try again.'); + yield $this->web_echo_async('ERROR: '.$e->getMessage().'. Try again.'); } catch (\danog\MadelineProto\Exception $e) { - $this->web_echo('ERROR: '.$e->getMessage().'. Try again.'); + yield $this->web_echo_async('ERROR: '.$e->getMessage().'. Try again.'); } } @@ -147,11 +151,11 @@ trait Start { try { yield $this->bot_login_async($_POST['token']); - $this->web_echo(); + yield $this->web_echo_async(); } catch (\danog\MadelineProto\RPCErrorException $e) { - $this->web_echo('ERROR: '.$e->getMessage().'. Try again.'); + yield $this->web_echo_async('ERROR: '.$e->getMessage().'. Try again.'); } catch (\danog\MadelineProto\Exception $e) { - $this->web_echo('ERROR: '.$e->getMessage().'. Try again.'); + yield $this->web_echo_async('ERROR: '.$e->getMessage().'. Try again.'); } } } diff --git a/src/danog/MadelineProto/Wrappers/Templates.php b/src/danog/MadelineProto/Wrappers/Templates.php index 0a122dbf..8f3cef2f 100644 --- a/src/danog/MadelineProto/Wrappers/Templates.php +++ b/src/danog/MadelineProto/Wrappers/Templates.php @@ -19,33 +19,36 @@ namespace danog\MadelineProto\Wrappers; +use function Amp\ByteStream\getOutput; + trait Templates { - public function web_echo($message = '') + public function web_echo_async($message = '') { + $stdout = getOutput(); switch ($this->authorized) { case self::NOT_LOGGED_IN: if (isset($_POST['type'])) { if ($_POST['type'] === 'phone') { - echo $this->web_echo_template('Enter your phone number<br><b>'.$message.'</b>', '<input type="text" name="phone_number" placeholder="Phone number" required/>'); + yield $stdout->write($this->web_echo_template('Enter your phone number<br><b>'.$message.'</b>', '<input type="text" name="phone_number" placeholder="Phone number" required/>')); } else { - echo $this->web_echo_template('Enter your bot token<br><b>'.$message.'</b>', '<input type="text" name="token" placeholder="Bot token" required/>'); + yield $stdout->write($this->web_echo_template('Enter your bot token<br><b>'.$message.'</b>', '<input type="text" name="token" placeholder="Bot token" required/>')); } } else { - echo $this->web_echo_template('Do you want to login as user or bot?<br><b>'.$message.'</b>', '<select name="type"><option value="phone">User</option><option value="bot">Bot</option></select>'); + yield $stdout->write($this->web_echo_template('Do you want to login as user or bot?<br><b>'.$message.'</b>', '<select name="type"><option value="phone">User</option><option value="bot">Bot</option></select>')); } break; case self::WAITING_CODE: - echo $this->web_echo_template('Enter your code<br><b>'.$message.'</b>', '<input type="text" name="phone_code" placeholder="Phone code" required/>'); + yield $stdout->write($this->web_echo_template('Enter your code<br><b>'.$message.'</b>', '<input type="text" name="phone_code" placeholder="Phone code" required/>')); break; case self::WAITING_PASSWORD: - echo $this->web_echo_template('Enter your password<br><b>'.$message.'</b>', '<input type="password" name="password" placeholder="Hint: '.$this->authorization['hint'].'" required/>'); + yield $stdout->write($this->web_echo_template('Enter your password<br><b>'.$message.'</b>', '<input type="password" name="password" placeholder="Hint: '.$this->authorization['hint'].'" required/>')); break; case self::WAITING_SIGNUP: - echo $this->web_echo_template('Sign up please<br><b>'.$message.'</b>', '<input type="text" name="first_name" placeholder="First name" required/><input type="text" name="last_name" placeholder="Last name"/>'); + yield $stdout->write($this->web_echo_template('Sign up please<br><b>'.$message.'</b>', '<input type="text" name="first_name" placeholder="First name" required/><input type="text" name="last_name" placeholder="Last name"/>')); break; } } diff --git a/tests/testing.php b/tests/testing.php index 0d2c771d..da9a68bd 100755 --- a/tests/testing.php +++ b/tests/testing.php @@ -43,7 +43,6 @@ $settings = json_decode(getenv('MTPROTO_SETTINGS'), true) ?: []; */ echo 'Loading MadelineProto...'.PHP_EOL; $MadelineProto = new \danog\MadelineProto\API(getcwd().'/testing.madeline', $settings); -var_dump($MadelineProto->get_dialogs_full()); try { $MadelineProto->get_self();