Major fixes and refactoring of wrapper APIs

This commit is contained in:
Daniil Gentili 2019-05-22 17:35:16 +02:00
parent 46c4e222db
commit b339a9d849
28 changed files with 615 additions and 635 deletions

View File

@ -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

View File

@ -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",

View File

@ -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();

View File

@ -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);

View File

@ -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();
}

View File

@ -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;
})());
}
}

View File

@ -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]);
}
}

View File

@ -0,0 +1,53 @@
<?php
/**
* Async constructor abstract class.
*
* This file is part of MadelineProto.
* MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
* MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Affero General Public License for more details.
* You should have received a copy of the GNU General Public License along with MadelineProto.
* If not, see <http://www.gnu.org/licenses/>.
*
* @author Daniil Gentili <daniil@daniil.it>
* @copyright 2016-2018 Daniil Gentili <daniil@daniil.it>
* @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 <daniil@daniil.it>
*/
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;
});
}
}

View File

@ -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__;

View File

@ -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']) {

View File

@ -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]);
}
}

View File

@ -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, '<br>'.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)

View File

@ -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();

View File

@ -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();
}

View File

@ -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);

View File

@ -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;
}
}

View File

@ -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',

View File

@ -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)) {

File diff suppressed because one or more lines are too long

View File

@ -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('</title>', explode('<title>', $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('</title>', explode('<title>', $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;
}
}

View File

@ -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;
}
}

View File

@ -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);

View File

@ -252,6 +252,7 @@ trait Tools
}
});
}
return $promise;
}
public function rethrow($e)
{

View File

@ -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.');
}
}
}

View File

@ -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>
');
'));
}
}
}

View File

@ -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.');
}
}
}

View File

@ -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;
}
}

View File

@ -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();