Yield from all the things
This commit is contained in:
parent
b842c8f7e5
commit
000839a1b5
@ -19,7 +19,7 @@
|
||||
|
||||
namespace phpseclib\Math;
|
||||
|
||||
if (PHP_MAJOR_VERSION < 7 && !((\class_exists(\Phar::class) && \Phar::running()) || \defined('TESTING_VERSIONS'))) {
|
||||
if (PHP_MAJOR_VERSION < 7 && !(\class_exists(\Phar::class) && \Phar::running() || \defined('TESTING_VERSIONS'))) {
|
||||
throw new \Exception('MadelineProto requires php 7 to run natively, use phar.madelineproto.xyz to run on PHP 5.6');
|
||||
}
|
||||
if (\defined('HHVM_VERSION')) {
|
||||
@ -32,7 +32,6 @@ if (\defined('HHVM_VERSION')) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class BigIntegor
|
||||
{
|
||||
}
|
||||
|
@ -22,7 +22,8 @@ if (!\class_exists('ReflectionGenerator')) {
|
||||
}
|
||||
public function getFunction(): ReflectionFunctionAbstract
|
||||
{
|
||||
return new ReflectionFunction(function () {});
|
||||
return new ReflectionFunction(function () {
|
||||
});
|
||||
}
|
||||
public function getThis(): object
|
||||
{
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Yield return value PHP5 polyfill.
|
||||
*
|
||||
@ -18,12 +19,10 @@
|
||||
class YieldReturnValue
|
||||
{
|
||||
private $value;
|
||||
|
||||
public function __construct($value)
|
||||
{
|
||||
$this->value = $value;
|
||||
}
|
||||
|
||||
public function getReturn()
|
||||
{
|
||||
return $this->value;
|
||||
|
@ -21,7 +21,6 @@ namespace danog\MadelineProto;
|
||||
|
||||
use Amp\Deferred;
|
||||
use Amp\Promise;
|
||||
|
||||
use function Amp\File\exists;
|
||||
use function Amp\File\get;
|
||||
use function Amp\File\put;
|
||||
@ -67,7 +66,6 @@ class API extends InternalDoc
|
||||
public $asyncAPIPromise;
|
||||
private $oldInstance = false;
|
||||
private $destructing = false;
|
||||
|
||||
/**
|
||||
* Magic constructor function.
|
||||
*
|
||||
@ -94,7 +92,6 @@ class API extends InternalDoc
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Async constructor function.
|
||||
*
|
||||
@ -108,22 +105,18 @@ class API extends InternalDoc
|
||||
{
|
||||
if (\is_string($params)) {
|
||||
Logger::constructorFromSettings($settings);
|
||||
|
||||
$realpaths = Serialization::realpaths($params);
|
||||
$this->session = $realpaths['file'];
|
||||
|
||||
if (yield exists($realpaths['file'])) {
|
||||
Logger::log('Waiting for shared lock of serialization lockfile...');
|
||||
$unlock = yield Tools::flock($realpaths['lockfile'], LOCK_SH);
|
||||
Logger::log('Shared lock acquired, deserializing...');
|
||||
|
||||
try {
|
||||
$tounserialize = yield get($realpaths['file']);
|
||||
} finally {
|
||||
$unlock();
|
||||
}
|
||||
\danog\MadelineProto\Magic::classExists();
|
||||
|
||||
try {
|
||||
$unserialized = \unserialize($tounserialize);
|
||||
} catch (\danog\MadelineProto\Bug74586Exception $e) {
|
||||
@ -165,12 +158,10 @@ class API extends InternalDoc
|
||||
$tounserialize = \str_replace('C:26:"phpseclib3\\Math\\BigInteger"', 'C:24:"tgseclib\\Math\\BigInteger"', $tounserialize);
|
||||
$changed = true;
|
||||
}
|
||||
|
||||
Logger::log((string) $e, Logger::ERROR);
|
||||
if (!$changed) {
|
||||
throw $e;
|
||||
}
|
||||
|
||||
try {
|
||||
$unserialized = \danog\Serialization::unserialize($tounserialize);
|
||||
} catch (\Throwable $e) {
|
||||
@ -189,7 +180,6 @@ class API extends InternalDoc
|
||||
$this->web_api_template = $unserialized->web_api_template;
|
||||
$this->my_telegram_org_wrapper = $unserialized->my_telegram_org_wrapper;
|
||||
$this->getting_api_id = $unserialized->getting_api_id;
|
||||
|
||||
if (isset($unserialized->API)) {
|
||||
$this->API = $unserialized->API;
|
||||
$this->APIFactory();
|
||||
@ -208,7 +198,6 @@ class API extends InternalDoc
|
||||
$params = $settings;
|
||||
}
|
||||
Logger::constructorFromSettings($settings);
|
||||
|
||||
if (!isset($params['app_info']['api_id']) || !$params['app_info']['api_id']) {
|
||||
$app = yield $this->APIStart($params);
|
||||
$params['app_info']['api_id'] = $app['api_id'];
|
||||
@ -226,7 +215,6 @@ class API extends InternalDoc
|
||||
//\danog\MadelineProto\Logger::log('Pong: '.$pong['ping_id'], Logger::ULTRA_VERBOSE);
|
||||
\danog\MadelineProto\Logger::log(\danog\MadelineProto\Lang::$current_lang['madelineproto_ready'], Logger::NOTICE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable or disable async.
|
||||
*
|
||||
@ -237,14 +225,12 @@ class API extends InternalDoc
|
||||
public function async(bool $async): void
|
||||
{
|
||||
$this->async = $async;
|
||||
|
||||
if ($this->API) {
|
||||
if ($this->API->event_handler && \class_exists($this->API->event_handler) && \is_subclass_of($this->API->event_handler, '\danog\MadelineProto\EventHandler')) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Destruct function.
|
||||
*
|
||||
@ -270,7 +256,6 @@ class API extends InternalDoc
|
||||
}
|
||||
//restore_error_handler();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sleep function.
|
||||
*
|
||||
@ -282,8 +267,6 @@ class API extends InternalDoc
|
||||
{
|
||||
return ['API', 'web_api_template', 'getting_api_id', 'my_telegram_org_wrapper'];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Custom fast getSelf.
|
||||
*
|
||||
@ -295,7 +278,6 @@ class API extends InternalDoc
|
||||
{
|
||||
return isset($this->API) && isset($this->API->authorization['user']) ? $this->API->authorization['user'] : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Init API wrapper.
|
||||
*
|
||||
@ -322,7 +304,6 @@ class API extends InternalDoc
|
||||
$this->methods = [];
|
||||
foreach ($methods as $method) {
|
||||
$actual_method = $method;
|
||||
|
||||
if ($method == 'methodCallAsyncRead') {
|
||||
$method = 'methodCall';
|
||||
} elseif (\stripos($method, 'async') !== false) {
|
||||
@ -340,14 +321,12 @@ class API extends InternalDoc
|
||||
$this->methods[\strtolower(Tools::fromCamelCase($method))] = $actual_method;
|
||||
}
|
||||
}
|
||||
|
||||
$this->API->wrapper = $this;
|
||||
if ($this->API->event_handler && \class_exists($this->API->event_handler) && \is_subclass_of($this->API->event_handler, '\danog\MadelineProto\EventHandler')) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get full list of MTProto and API methods.
|
||||
*
|
||||
@ -362,10 +341,8 @@ class API extends InternalDoc
|
||||
foreach ($this->API->methods->by_id as $method) {
|
||||
$methods[] = $method['method'];
|
||||
}
|
||||
|
||||
return \array_merge($methods, \get_class_methods($this->API));
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize session.
|
||||
*
|
||||
@ -377,12 +354,11 @@ class API extends InternalDoc
|
||||
*/
|
||||
public function serialize(string $filename = ''): Promise
|
||||
{
|
||||
return Tools::callFork((function () use ($filename) {
|
||||
return Tools::callFork((function () use ($filename): \Generator {
|
||||
if (empty($filename)) {
|
||||
$filename = $this->session;
|
||||
}
|
||||
//Logger::log(\danog\MadelineProto\Lang::$current_lang['serializing_madelineproto']);
|
||||
|
||||
if ($filename == '') {
|
||||
return;
|
||||
}
|
||||
@ -399,11 +375,8 @@ class API extends InternalDoc
|
||||
$this->serialized = \time();
|
||||
$realpaths = Serialization::realpaths($filename);
|
||||
//Logger::log('Waiting for exclusive lock of serialization lockfile...');
|
||||
|
||||
$unlock = yield Tools::flock($realpaths['lockfile'], LOCK_EX);
|
||||
|
||||
//Logger::log('Lock acquired, serializing');
|
||||
|
||||
try {
|
||||
if (!$this->getting_api_id) {
|
||||
$update_closure = $this->API->settings['updates']['callback'];
|
||||
@ -425,7 +398,6 @@ class API extends InternalDoc
|
||||
$unlock();
|
||||
}
|
||||
//Logger::log('Done serializing');
|
||||
|
||||
return $wrote;
|
||||
})());
|
||||
}
|
||||
|
@ -26,10 +26,9 @@ class Absolute
|
||||
{
|
||||
public static function absolute($file)
|
||||
{
|
||||
if (($file[0] !== '/') && ($file[1] !== ':') && !\in_array(\substr($file, 0, 4), ['phar', 'http'])) {
|
||||
if ($file[0] !== '/' && $file[1] !== ':' && !\in_array(\substr($file, 0, 4), ['phar', 'http'])) {
|
||||
$file = Magic::getcwd() . '/' . $file;
|
||||
}
|
||||
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
@ -61,21 +61,18 @@ abstract class AbstractAPIFactory extends AsyncConstruct
|
||||
* @var Promise
|
||||
*/
|
||||
public $asyncAPIPromise;
|
||||
|
||||
/**
|
||||
* Method list.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected $methods = [];
|
||||
|
||||
public function __construct($namespace, &$API, &$async)
|
||||
{
|
||||
$this->namespace = $namespace . '.';
|
||||
$this->API =& $API;
|
||||
$this->async =& $async;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable or disable async.
|
||||
*
|
||||
@ -87,7 +84,6 @@ abstract class AbstractAPIFactory extends AsyncConstruct
|
||||
{
|
||||
$this->async = $async;
|
||||
}
|
||||
|
||||
/**
|
||||
* Call async wrapper function.
|
||||
*
|
||||
@ -101,25 +97,21 @@ abstract class AbstractAPIFactory extends AsyncConstruct
|
||||
public function __call(string $name, array $arguments)
|
||||
{
|
||||
$yielded = Tools::call($this->__call_async($name, $arguments));
|
||||
$async = $this->lua === false && (\is_array(\end($arguments)) && isset(\end($arguments)['async']) ? \end($arguments)['async'] : ($this->async && $name !== 'loop'));
|
||||
|
||||
$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 Tools::wait($yielded);
|
||||
}
|
||||
|
||||
try {
|
||||
$yielded = Tools::wait($yielded);
|
||||
Lua::convertObjects($yielded);
|
||||
|
||||
return $yielded;
|
||||
} catch (\Throwable $e) {
|
||||
return ['error_code' => $e->getCode(), 'error' => $e->getMessage()];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Call async wrapper function.
|
||||
*
|
||||
@ -159,7 +151,6 @@ abstract class AbstractAPIFactory extends AsyncConstruct
|
||||
yield $this->API->initAsynchronously();
|
||||
$this->API->logger->logger('Finished init asynchronously');
|
||||
}
|
||||
|
||||
$lower_name = \strtolower($name);
|
||||
if ($this->namespace !== '' || !isset($this->methods[$lower_name])) {
|
||||
$name = $this->namespace . $name;
|
||||
@ -167,12 +158,10 @@ abstract class AbstractAPIFactory extends AsyncConstruct
|
||||
$aargs['apifactory'] = true;
|
||||
$aargs['datacenter'] = $this->API->datacenter->curdc;
|
||||
$args = isset($arguments[0]) && \is_array($arguments[0]) ? $arguments[0] : [];
|
||||
|
||||
return yield $this->API->methodCallAsyncRead($name, $args, $aargs);
|
||||
}
|
||||
return yield $this->methods[$lower_name](...$arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get attribute.
|
||||
*
|
||||
@ -189,16 +178,13 @@ abstract class AbstractAPIFactory extends AsyncConstruct
|
||||
}
|
||||
if ($name === 'settings') {
|
||||
$this->API->flushSettings = true;
|
||||
|
||||
return $this->API->settings;
|
||||
}
|
||||
if ($name === 'logger') {
|
||||
return $this->API->logger;
|
||||
}
|
||||
|
||||
return $this->API->storage[$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* Set an attribute.
|
||||
*
|
||||
@ -218,13 +204,10 @@ abstract class AbstractAPIFactory extends AsyncConstruct
|
||||
if ($this->API->asyncInitPromise) {
|
||||
$this->API->init();
|
||||
}
|
||||
|
||||
return $this->API->__construct(\array_replace_recursive($this->API->settings, $value));
|
||||
}
|
||||
|
||||
return $this->API->storage[$name] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether an attribute exists.
|
||||
*
|
||||
@ -237,10 +220,8 @@ abstract class AbstractAPIFactory extends AsyncConstruct
|
||||
if ($this->asyncAPIPromise) {
|
||||
Tools::wait($this->asyncAPIPromise);
|
||||
}
|
||||
|
||||
return isset($this->API->storage[$name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unset attribute.
|
||||
*
|
||||
|
@ -27,7 +27,6 @@ use phpDocumentor\Reflection\DocBlockFactory;
|
||||
class AnnotationsBuilder
|
||||
{
|
||||
use Tools;
|
||||
|
||||
public function __construct(Logger $logger, array $settings, string $output, array $reflectionClasses, string $namespace)
|
||||
{
|
||||
$this->reflectionClasses = $reflectionClasses;
|
||||
@ -43,14 +42,12 @@ class AnnotationsBuilder
|
||||
$this->settings = $settings;
|
||||
$this->output = $output;
|
||||
}
|
||||
|
||||
public function mkAnnotations()
|
||||
{
|
||||
\danog\MadelineProto\Logger::log('Generating annotations...', \danog\MadelineProto\Logger::NOTICE);
|
||||
$this->setProperties();
|
||||
$this->createInternalClasses();
|
||||
}
|
||||
|
||||
/**
|
||||
* Open file of class APIFactory
|
||||
* Insert properties
|
||||
@ -77,7 +74,6 @@ class AnnotationsBuilder
|
||||
}
|
||||
\file_put_contents($filename, $content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create internalDoc.
|
||||
*
|
||||
@ -88,23 +84,19 @@ class AnnotationsBuilder
|
||||
\danog\MadelineProto\Logger::log('Creating internal classes...', \danog\MadelineProto\Logger::NOTICE);
|
||||
$handle = \fopen($this->output, 'w');
|
||||
\fwrite($handle, "<?php namespace {$this->namespace}; class InternalDoc extends APIFactory {}");
|
||||
|
||||
$class = new \ReflectionClass($this->reflectionClasses['API']);
|
||||
$methods = $class->getMethods(\ReflectionMethod::IS_STATIC | \ReflectionMethod::IS_PUBLIC);
|
||||
$ignoreMethods = ['fetchserializableobject'];
|
||||
foreach ($methods as $method) {
|
||||
$ignoreMethods[$method->getName()] = $method->getName();
|
||||
}
|
||||
|
||||
$class = new \ReflectionClass(TLCallback::class);
|
||||
$methods = $class->getMethods(\ReflectionMethod::IS_STATIC | \ReflectionMethod::IS_PUBLIC);
|
||||
foreach ($methods as $method) {
|
||||
$ignoreMethods[$method->getName()] = $method->getName();
|
||||
}
|
||||
|
||||
\fclose($handle);
|
||||
$handle = \fopen($this->output, 'w');
|
||||
|
||||
$internalDoc = [];
|
||||
foreach ($this->TL->getMethods()->by_id as $id => $data) {
|
||||
if (!\strpos($data['method'], '.')) {
|
||||
@ -115,7 +107,6 @@ class AnnotationsBuilder
|
||||
continue;
|
||||
}
|
||||
$internalDoc[$namespace][$method]['title'] = \str_replace(['](../', '.md'], ['](https://docs.madelineproto.xyz/API_docs/', '.html'], Lang::$current_lang["method_{$data['method']}"] ?? '');
|
||||
|
||||
$type = \str_ireplace(['vector<', '>'], [' of ', '[]'], $data['type']);
|
||||
foreach ($data['params'] as $param) {
|
||||
if (\in_array($param['name'], ['flags', 'random_id', 'random_bytes'])) {
|
||||
@ -133,31 +124,25 @@ class AnnotationsBuilder
|
||||
$param['type'] = 'Vector t';
|
||||
$param['subtype'] = 'int';
|
||||
}
|
||||
|
||||
$stype = 'type';
|
||||
if (isset($param['subtype'])) {
|
||||
$stype = 'subtype';
|
||||
}
|
||||
|
||||
$ptype = $param[$stype];
|
||||
switch ($ptype) {
|
||||
case 'true':
|
||||
case 'false':
|
||||
$ptype = 'boolean';
|
||||
}
|
||||
$ptype = $stype === 'type' ? $ptype : "[$ptype]";
|
||||
$opt = ($param['pow'] ?? false) ? 'Optional: ' : '';
|
||||
$internalDoc[$namespace][$method]['attr'][$param['name']] = [
|
||||
'type' => $ptype,
|
||||
'description' => \str_replace(['](../', '.md'], ['](https://docs.madelineproto.xyz/API_docs/', '.html'], $opt.(Lang::$current_lang["method_{$data['method']}_param_{$param['name']}_type_{$param['type']}"] ?? ''))
|
||||
];
|
||||
$ptype = $stype === 'type' ? $ptype : "[{$ptype}]";
|
||||
$opt = $param['pow'] ?? false ? 'Optional: ' : '';
|
||||
$internalDoc[$namespace][$method]['attr'][$param['name']] = ['type' => $ptype, 'description' => \str_replace(['](../', '.md'], ['](https://docs.madelineproto.xyz/API_docs/', '.html'], $opt . (Lang::$current_lang["method_{$data['method']}_param_{$param['name']}_type_{$param['type']}"] ?? ''))];
|
||||
}
|
||||
if ($type === 'Bool') {
|
||||
$type = \strtolower($type);
|
||||
}
|
||||
$internalDoc[$namespace][$method]['return'] = $type;
|
||||
}
|
||||
|
||||
$class = new \ReflectionClass($this->reflectionClasses['MTProto']);
|
||||
$methods = $class->getMethods(\ReflectionMethod::IS_STATIC | \ReflectionMethod::IS_PUBLIC);
|
||||
foreach ($methods as $key => $method) {
|
||||
@ -183,15 +168,13 @@ class AnnotationsBuilder
|
||||
continue;
|
||||
}
|
||||
$static = $method->isStatic();
|
||||
|
||||
if (!$static) {
|
||||
$code = \file_get_contents($method->getFileName());
|
||||
$code = \implode("\n", \array_slice(\explode("\n", $code), $method->getStartLine(), $method->getEndLine() - $method->getStartLine()));
|
||||
if (\strpos($code, '$this') === false) {
|
||||
Logger::log("$name should be STATIC!", Logger::FATAL_ERROR);
|
||||
Logger::log("{$name} should be STATIC!", Logger::FATAL_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
if ($name == 'methodCallAsyncRead') {
|
||||
$name = 'methodCall';
|
||||
} elseif (\stripos($name, 'async') !== false) {
|
||||
@ -203,10 +186,8 @@ class AnnotationsBuilder
|
||||
}
|
||||
$name = Tools::fromSnakeCase($name);
|
||||
$name = \str_ireplace(['mtproto', 'api'], ['MTProto', 'API'], $name);
|
||||
|
||||
$doc = 'public function ';
|
||||
$doc .= $name;
|
||||
|
||||
$doc .= '(';
|
||||
$paramList = '';
|
||||
$hasVariadic = false;
|
||||
@ -243,8 +224,6 @@ class AnnotationsBuilder
|
||||
}
|
||||
}
|
||||
$doc .= ', ';
|
||||
|
||||
|
||||
if ($param->isVariadic()) {
|
||||
$hasVariadic = true;
|
||||
$paramList .= '...';
|
||||
@ -271,35 +250,29 @@ class AnnotationsBuilder
|
||||
$doc .= $type->getName() === 'self' ? $this->reflectionClasses['API'] : $type->getName();
|
||||
$async = false;
|
||||
}
|
||||
$finalParamList = $hasVariadic ? "Tools::arr($paramList)" : "[$paramList]";
|
||||
|
||||
$finalParamList = $hasVariadic ? "Tools::arr({$paramList})" : "[{$paramList}]";
|
||||
$ret = $type && \in_array($type->getName(), ['self', 'void']) ? '' : 'return';
|
||||
|
||||
$doc .= "\n{\n";
|
||||
if ($async) {
|
||||
$doc .= " $ret \$this->__call(__FUNCTION__, $finalParamList);\n";
|
||||
$doc .= " {$ret} \$this->__call(__FUNCTION__, {$finalParamList});\n";
|
||||
} elseif (!$static) {
|
||||
$doc .= " $ret \$this->API->$name($paramList);\n";
|
||||
$doc .= " {$ret} \$this->API->{$name}({$paramList});\n";
|
||||
} else {
|
||||
$doc .= " $ret \\".$method->getDeclaringClass()->getName()."::".$name."($paramList);\n";
|
||||
$doc .= " {$ret} \\" . $method->getDeclaringClass()->getName() . "::" . $name . "({$paramList});\n";
|
||||
}
|
||||
if (!$ret && $type->getName() === 'self') {
|
||||
$doc .= " return \$this;\n";
|
||||
}
|
||||
$doc .= "}\n";
|
||||
|
||||
|
||||
if (!$method->getDocComment()) {
|
||||
Logger::log("$name has no PHPDOC!", Logger::FATAL_ERROR);
|
||||
Logger::log("{$name} has no PHPDOC!", Logger::FATAL_ERROR);
|
||||
}
|
||||
if (!$type) {
|
||||
Logger::log("$name has no return type!", Logger::FATAL_ERROR);
|
||||
Logger::log("{$name} has no return type!", Logger::FATAL_ERROR);
|
||||
}
|
||||
|
||||
$internalDoc['InternalDoc'][$name]['method'] = $method->getDocComment() ?? '';
|
||||
$internalDoc['InternalDoc'][$name]['method'] .= "\n " . \implode("\n ", \explode("\n", $doc));
|
||||
}
|
||||
|
||||
\fwrite($handle, "<?php\n");
|
||||
\fwrite($handle, "/**\n");
|
||||
\fwrite($handle, " * This file is automatic generated by build_docs.php file\n");
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Async constructor abstract class.
|
||||
*
|
||||
@ -35,7 +36,6 @@ class AsyncConstruct
|
||||
* @var Promise
|
||||
*/
|
||||
public $asyncInitPromise;
|
||||
|
||||
/**
|
||||
* Blockingly init.
|
||||
*
|
||||
@ -47,7 +47,6 @@ class AsyncConstruct
|
||||
Tools::wait($this->asyncInitPromise);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Asynchronously init.
|
||||
*
|
||||
@ -59,7 +58,6 @@ class AsyncConstruct
|
||||
yield $this->asyncInitPromise;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set init promise.
|
||||
*
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Async parameters class.
|
||||
*
|
||||
@ -33,7 +34,6 @@ class AsyncParameters
|
||||
* @var callable
|
||||
*/
|
||||
private $callable;
|
||||
|
||||
/**
|
||||
* Create async parameters.
|
||||
*
|
||||
@ -43,8 +43,6 @@ class AsyncParameters
|
||||
{
|
||||
$this->callable = $callable;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create async parameters.
|
||||
*
|
||||
@ -54,7 +52,6 @@ class AsyncParameters
|
||||
{
|
||||
$this->callable = $callable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get parameters asynchronously.
|
||||
*
|
||||
@ -63,7 +60,6 @@ class AsyncParameters
|
||||
public function getParameters()
|
||||
{
|
||||
$callable = $this->callable;
|
||||
|
||||
return $callable();
|
||||
}
|
||||
}
|
||||
|
@ -33,19 +33,15 @@ class CombinedAPI
|
||||
public $serialization_interval = 30;
|
||||
public $serialized = 0;
|
||||
protected $async;
|
||||
|
||||
public function __magic_construct($session, $paths = [])
|
||||
{
|
||||
\set_error_handler(['\\danog\\MadelineProto\\Exception', 'ExceptionErrorHandler']);
|
||||
\danog\MadelineProto\Magic::classExists();
|
||||
|
||||
$realpaths = Serialization::realpaths($session);
|
||||
$this->session = $realpaths['file'];
|
||||
|
||||
foreach ($paths as $path => $settings) {
|
||||
$this->addInstance($path, $settings);
|
||||
}
|
||||
|
||||
if (\file_exists($realpaths['file'])) {
|
||||
if (!\file_exists($realpaths['lockfile'])) {
|
||||
\touch($realpaths['lockfile']);
|
||||
@ -55,7 +51,6 @@ class CombinedAPI
|
||||
\danog\MadelineProto\Logger::log('Waiting for shared lock of serialization lockfile...');
|
||||
\flock($realpaths['lockfile'], LOCK_SH);
|
||||
\danog\MadelineProto\Logger::log('Shared lock acquired, deserializing...');
|
||||
|
||||
try {
|
||||
$tounserialize = \file_get_contents($realpaths['file']);
|
||||
} finally {
|
||||
@ -63,14 +58,11 @@ class CombinedAPI
|
||||
\fclose($realpaths['lockfile']);
|
||||
}
|
||||
$deserialized = \unserialize($tounserialize);
|
||||
|
||||
/*foreach ($deserialized['instance_paths'] as $path) {
|
||||
$this->addInstance($path, isset($paths[$path]) ? $paths[$path] : []);
|
||||
}*/
|
||||
|
||||
$this->event_handler = $deserialized['event_handler'];
|
||||
$this->event_handler_instance = $deserialized['event_handler_instance'];
|
||||
|
||||
if ($this->event_handler !== null) {
|
||||
$this->setEventHandler($this->event_handler);
|
||||
}
|
||||
@ -79,29 +71,23 @@ class CombinedAPI
|
||||
$this->addInstance($path, $settings);
|
||||
}
|
||||
}
|
||||
|
||||
public function addInstance($path, $settings = [])
|
||||
{
|
||||
if (isset($this->instances[$path]) && isset($this->instance_paths[$path])) {
|
||||
if (isset($this->event_handler_instance)) {
|
||||
$this->event_handler_instance->referenceInstance($path);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
\danog\MadelineProto\Logger::constructor(3);
|
||||
\danog\MadelineProto\Logger::log("INSTANTIATING $path...");
|
||||
\danog\MadelineProto\Logger::log("INSTANTIATING {$path}...");
|
||||
$instance = new \danog\MadelineProto\API($path, $settings);
|
||||
|
||||
$this->instance_paths[$path] = $path;
|
||||
$this->instances[$path] = $instance;
|
||||
|
||||
if (isset($this->event_handler_instance)) {
|
||||
$this->event_handler_instance->referenceInstance($path);
|
||||
}
|
||||
}
|
||||
|
||||
public function removeInstance($path)
|
||||
{
|
||||
if (isset($this->instance_paths[$path])) {
|
||||
@ -110,27 +96,22 @@ class CombinedAPI
|
||||
if (isset($this->instances[$path])) {
|
||||
unset($this->instances[$path]);
|
||||
}
|
||||
|
||||
if (isset($this->event_handler_instance)) {
|
||||
$this->event_handler_instance->removeInstance($path);
|
||||
}
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
if (\danog\MadelineProto\Magic::$has_thread && \is_object(\Thread::getCurrentThread()) || Magic::isFork()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->serialize();
|
||||
}
|
||||
|
||||
public function serialize($filename = '')
|
||||
{
|
||||
/*foreach ($this->instances as $instance) {
|
||||
$instance->serialize();
|
||||
}*/
|
||||
|
||||
if (\is_null($this->session)) {
|
||||
return;
|
||||
}
|
||||
@ -147,7 +128,6 @@ class CombinedAPI
|
||||
\danog\MadelineProto\Logger::log('Waiting for exclusive lock of serialization lockfile...');
|
||||
\flock($realpaths['lockfile'], LOCK_EX);
|
||||
\danog\MadelineProto\Logger::log('Lock acquired, serializing');
|
||||
|
||||
try {
|
||||
$wrote = \file_put_contents($realpaths['tempfile'], \serialize(['event_handler' => $this->event_handler, 'event_handler_instance' => $this->event_handler_instance, 'instance_paths' => $this->instance_paths]));
|
||||
\rename($realpaths['tempfile'], $realpaths['file']);
|
||||
@ -155,12 +135,9 @@ class CombinedAPI
|
||||
\flock($realpaths['lockfile'], LOCK_UN);
|
||||
\fclose($realpaths['lockfile']);
|
||||
}
|
||||
|
||||
$this->serialized = \time();
|
||||
|
||||
return $wrote;
|
||||
}
|
||||
|
||||
public $event_handler;
|
||||
private $event_handler_instance;
|
||||
private $event_handler_methods = [];
|
||||
@ -170,20 +147,17 @@ class CombinedAPI
|
||||
}
|
||||
public function setEventHandler($event_handler)
|
||||
{
|
||||
if (!\class_exists($event_handler) || !\is_subclass_of($event_handler, '\danog\MadelineProto\CombinedEventHandler')) {
|
||||
if (!\class_exists($event_handler) || !\is_subclass_of($event_handler, '\\danog\\MadelineProto\\CombinedEventHandler')) {
|
||||
throw new \danog\MadelineProto\Exception('Wrong event handler was defined');
|
||||
}
|
||||
|
||||
$this->event_handler = $event_handler;
|
||||
|
||||
if (!($this->event_handler_instance instanceof $this->event_handler)) {
|
||||
if (!$this->event_handler_instance instanceof $this->event_handler) {
|
||||
$class_name = $this->event_handler;
|
||||
$this->event_handler_instance = new $class_name($this);
|
||||
} else {
|
||||
$this->event_handler_instance->__construct($this);
|
||||
}
|
||||
$this->event_handler_methods = [];
|
||||
|
||||
foreach (\get_class_methods($this->event_handler) as $method) {
|
||||
if ($method === 'onLoop') {
|
||||
$this->loop_callback = [$this->event_handler_instance, 'onLoop'];
|
||||
@ -199,16 +173,13 @@ class CombinedAPI
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function eventUpdateHandler($update, $instance)
|
||||
{
|
||||
if (isset($this->event_handler_methods[$update['_']])) {
|
||||
return $this->event_handler_methods[$update['_']]($update, $instance);
|
||||
}
|
||||
}
|
||||
|
||||
private $loop_callback;
|
||||
|
||||
public function async($async)
|
||||
{
|
||||
$this->async = $async;
|
||||
@ -216,22 +187,18 @@ class CombinedAPI
|
||||
$instance->async($async);
|
||||
}
|
||||
}
|
||||
|
||||
public function setLoopCallback($callback)
|
||||
{
|
||||
$this->loop_callback = $callback;
|
||||
}
|
||||
|
||||
public function getUpdates($params = [])
|
||||
{
|
||||
}
|
||||
|
||||
public function loop($max_forks = 0)
|
||||
{
|
||||
if (\is_callable($max_forks)) {
|
||||
return \danog\MadelineProto\Tools::wait($max_forks());
|
||||
}
|
||||
|
||||
$loops = [];
|
||||
foreach ($this->instances as $path => $instance) {
|
||||
\danog\MadelineProto\Tools::wait($instance->initAsynchronously());
|
||||
@ -250,12 +217,10 @@ class CombinedAPI
|
||||
}
|
||||
$loops[] = \danog\MadelineProto\Tools::call($instance->loop(0, ['async' => true]));
|
||||
}
|
||||
|
||||
Loop::repeat($this->serialization_interval * 1000, function () {
|
||||
\danog\MadelineProto\Logger::log('Serializing combined event handler');
|
||||
$this->serialize();
|
||||
});
|
||||
|
||||
\danog\MadelineProto\Logger::log('Started update loop', \danog\MadelineProto\Logger::NOTICE);
|
||||
\danog\MadelineProto\Tools::wait(all($loops));
|
||||
}
|
||||
|
@ -22,7 +22,6 @@ namespace danog\MadelineProto;
|
||||
abstract class CombinedEventHandler
|
||||
{
|
||||
private $CombinedAPI;
|
||||
|
||||
public function __construct($CombinedAPI)
|
||||
{
|
||||
$this->CombinedAPI = $CombinedAPI;
|
||||
@ -30,7 +29,6 @@ abstract class CombinedEventHandler
|
||||
$this->referenceInstance($path);
|
||||
}
|
||||
}
|
||||
|
||||
final public function __sleep()
|
||||
{
|
||||
$keys = \method_exists($this, '__magic_sleep') ? $this->__magic_sleep() : \get_object_vars($this);
|
||||
@ -46,15 +44,12 @@ abstract class CombinedEventHandler
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return \array_keys($keys);
|
||||
}
|
||||
|
||||
final public function referenceInstance($path)
|
||||
{
|
||||
$this->{$path} = $this->CombinedAPI->instances[$path];
|
||||
}
|
||||
|
||||
final public function removeInstance($path)
|
||||
{
|
||||
if (isset($this->{$path})) {
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Connection module.
|
||||
*
|
||||
@ -44,7 +45,6 @@ class Connection extends Session
|
||||
{
|
||||
use \danog\Serializable;
|
||||
use Tools;
|
||||
|
||||
/**
|
||||
* Writer loop.
|
||||
*
|
||||
@ -87,7 +87,6 @@ class Connection extends Session
|
||||
* @var ConnectionContext
|
||||
*/
|
||||
private $ctx;
|
||||
|
||||
/**
|
||||
* HTTP request count.
|
||||
*
|
||||
@ -100,14 +99,12 @@ class Connection extends Session
|
||||
* @var integer
|
||||
*/
|
||||
private $httpResCount = 0;
|
||||
|
||||
/**
|
||||
* Date of last chunk received.
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
private $lastChunk = 0;
|
||||
|
||||
/**
|
||||
* Logger instance.
|
||||
*
|
||||
@ -126,28 +123,24 @@ class Connection extends Session
|
||||
* @var DataCenterConnection
|
||||
*/
|
||||
protected $shared;
|
||||
|
||||
/**
|
||||
* DC ID.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $datacenter;
|
||||
|
||||
/**
|
||||
* Connection ID.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $id = 0;
|
||||
|
||||
/**
|
||||
* DC ID and connection ID concatenated.
|
||||
*
|
||||
* @var
|
||||
*/
|
||||
private $datacenterId = '';
|
||||
|
||||
/**
|
||||
* Whether this socket has to be reconnected.
|
||||
*
|
||||
@ -214,7 +207,6 @@ class Connection extends Session
|
||||
{
|
||||
$this->shared->reading($reading, $this->id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell the class that we have read a chunk of data from the socket.
|
||||
*
|
||||
@ -233,7 +225,6 @@ class Connection extends Session
|
||||
{
|
||||
return $this->lastChunk;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate a received HTTP response.
|
||||
*
|
||||
@ -270,7 +261,6 @@ class Connection extends Session
|
||||
{
|
||||
return $this->httpReqCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get connection ID.
|
||||
*
|
||||
@ -280,7 +270,6 @@ class Connection extends Session
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get datacenter concatenated with connection ID.
|
||||
*
|
||||
@ -290,7 +279,6 @@ class Connection extends Session
|
||||
{
|
||||
return $this->datacenterId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get connection context.
|
||||
*
|
||||
@ -300,7 +288,6 @@ class Connection extends Session
|
||||
{
|
||||
return $this->ctx;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if is an HTTP connection.
|
||||
*
|
||||
@ -310,7 +297,6 @@ class Connection extends Session
|
||||
{
|
||||
return \in_array($this->ctx->getStreamName(), [HttpStream::getName(), HttpsStream::getName()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if is a media connection.
|
||||
*
|
||||
@ -320,7 +306,6 @@ class Connection extends Session
|
||||
{
|
||||
return $this->ctx->isMedia();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if is a CDN connection.
|
||||
*
|
||||
@ -330,7 +315,6 @@ class Connection extends Session
|
||||
{
|
||||
return $this->ctx->isCDN();
|
||||
}
|
||||
|
||||
/**
|
||||
* Connects to a telegram DC using the specified protocol, proxy and connection parameters.
|
||||
*
|
||||
@ -344,16 +328,13 @@ class Connection extends Session
|
||||
$this->datacenter = $ctx->getDc();
|
||||
$this->datacenterId = $this->datacenter . '.' . $this->id;
|
||||
$this->API->logger->logger("Connecting to DC {$this->datacenterId}", \danog\MadelineProto\Logger::WARNING);
|
||||
|
||||
$ctx->setReadCallback([$this, 'haveRead']);
|
||||
$this->stream = yield $ctx->getStream();
|
||||
|
||||
if ($this->needsReconnect) {
|
||||
$this->needsReconnect = false;
|
||||
}
|
||||
$this->httpReqCount = 0;
|
||||
$this->httpResCount = 0;
|
||||
|
||||
if (!isset($this->writer)) {
|
||||
$this->writer = new WriteLoop($this);
|
||||
}
|
||||
@ -378,7 +359,6 @@ class Connection extends Session
|
||||
unset($this->new_outgoing[$message_id], $this->outgoing_messages[$message_id]);
|
||||
}
|
||||
}
|
||||
|
||||
$this->writer->start();
|
||||
$this->reader->start();
|
||||
if (!$this->checker->start()) {
|
||||
@ -389,7 +369,6 @@ class Connection extends Session
|
||||
$this->pinger->start();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send an MTProto message.
|
||||
*
|
||||
@ -427,17 +406,13 @@ class Connection extends Session
|
||||
public function sendMessage(array $message, bool $flush = true): \Generator
|
||||
{
|
||||
$deferred = new Deferred();
|
||||
|
||||
if (!isset($message['serialized_body'])) {
|
||||
$body = \is_object($message['body']) ? yield $message['body'] : $message['body'];
|
||||
|
||||
$refreshNext = isset($message['refreshNext']) && $message['refreshNext'];
|
||||
//$refreshNext = true;
|
||||
|
||||
if ($refreshNext) {
|
||||
$this->API->referenceDatabase->refreshNext(true);
|
||||
}
|
||||
|
||||
if ($message['method']) {
|
||||
$body = yield $this->API->getTL()->serializeMethod($message['_'], $body);
|
||||
} else {
|
||||
@ -450,16 +425,13 @@ class Connection extends Session
|
||||
$message['serialized_body'] = $body;
|
||||
unset($body);
|
||||
}
|
||||
|
||||
$message['send_promise'] = $deferred;
|
||||
$this->pending_outgoing[$this->pending_outgoing_key++] = $message;
|
||||
if ($flush && isset($this->writer)) {
|
||||
$this->writer->resume();
|
||||
}
|
||||
|
||||
return $deferred->promise();
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush pending packets.
|
||||
*
|
||||
@ -500,7 +472,6 @@ class Connection extends Session
|
||||
$this->API = $extra->getExtra();
|
||||
$this->logger = $this->API->logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get main instance.
|
||||
*
|
||||
@ -510,7 +481,6 @@ class Connection extends Session
|
||||
{
|
||||
return $this->API;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get shared connection instance.
|
||||
*
|
||||
@ -520,7 +490,6 @@ class Connection extends Session
|
||||
{
|
||||
return $this->shared;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disconnect from DC.
|
||||
*
|
||||
@ -549,7 +518,6 @@ class Connection extends Session
|
||||
}
|
||||
$this->API->logger->logger("Disconnected from DC {$this->datacenterId}");
|
||||
}
|
||||
|
||||
/**
|
||||
* Reconnect to DC.
|
||||
*
|
||||
@ -561,7 +529,6 @@ class Connection extends Session
|
||||
$this->disconnect(true);
|
||||
yield $this->API->datacenter->dcConnect($this->ctx->getDc(), $this->id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get name.
|
||||
*
|
||||
|
@ -37,16 +37,14 @@ class ContextConnector implements Connector
|
||||
$this->fromDns = $fromDns;
|
||||
$this->logger = $dataCenter->getAPI()->getLogger();
|
||||
}
|
||||
|
||||
public function connect(string $uri, ?ConnectContext $ctx = null, ?CancellationToken $token = null): Promise
|
||||
{
|
||||
return Tools::call((function () use ($uri, $ctx, $token) {
|
||||
$ctx = $ctx ?? new ConnectContext;
|
||||
$token = $token ?? new NullCancellationToken;
|
||||
|
||||
return Tools::call((function () use ($uri, $ctx, $token): \Generator {
|
||||
$ctx = $ctx ?? new ConnectContext();
|
||||
$token = $token ?? new NullCancellationToken();
|
||||
$ctxs = $this->dataCenter->generateContexts(0, $uri, $ctx);
|
||||
if (empty($ctxs)) {
|
||||
throw new Exception("No contexts for raw connection to URI $uri");
|
||||
throw new Exception("No contexts for raw connection to URI {$uri}");
|
||||
}
|
||||
foreach ($ctxs as $ctx) {
|
||||
/* @var $ctx \danog\MadelineProto\Stream\ConnectionContext */
|
||||
@ -55,7 +53,6 @@ class ContextConnector implements Connector
|
||||
$ctx->setCancellationToken($token);
|
||||
$result = yield $ctx->getStream();
|
||||
$this->logger->logger('OK!', \danog\MadelineProto\Logger::WARNING);
|
||||
|
||||
return $result->getSocket();
|
||||
} catch (\Throwable $e) {
|
||||
if (\MADELINEPROTO_TEST === 'pony') {
|
||||
@ -69,8 +66,7 @@ class ContextConnector implements Connector
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new \danog\MadelineProto\Exception("Could not connect to URI $uri");
|
||||
throw new \danog\MadelineProto\Exception("Could not connect to URI {$uri}");
|
||||
})());
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Coroutine (modified version of AMP Coroutine).
|
||||
*
|
||||
@ -57,24 +58,20 @@ final class Coroutine implements Promise, \ArrayAccess
|
||||
private $exception;
|
||||
/** @var mixed Promise success value when executing next coroutine step, null at all other times. */
|
||||
private $value;
|
||||
|
||||
/** @var ?self Reference to coroutine that started this coroutine */
|
||||
private $parentCoroutine;
|
||||
|
||||
/**
|
||||
* @param \Generator $generator
|
||||
*/
|
||||
public function __construct(\Generator $generator)
|
||||
{
|
||||
$this->generator = $generator;
|
||||
|
||||
try {
|
||||
$yielded = $this->generator->current();
|
||||
while (!$yielded instanceof Promise) {
|
||||
if ($yielded instanceof \YieldReturnValue) {
|
||||
$this->resolve($yielded->getReturn());
|
||||
$this->generator->next();
|
||||
|
||||
return;
|
||||
}
|
||||
if (!$this->generator->valid()) {
|
||||
@ -83,7 +80,6 @@ final class Coroutine implements Promise, \ArrayAccess
|
||||
} else {
|
||||
$this->resolve(null);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
if ($yielded instanceof \Generator) {
|
||||
@ -97,7 +93,6 @@ final class Coroutine implements Promise, \ArrayAccess
|
||||
}
|
||||
} catch (\Throwable $exception) {
|
||||
$this->fail($exception);
|
||||
|
||||
return;
|
||||
}
|
||||
/*
|
||||
@ -109,10 +104,8 @@ final class Coroutine implements Promise, \ArrayAccess
|
||||
$this->value = $value;
|
||||
if (!$this->immediate) {
|
||||
$this->immediate = true;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
do {
|
||||
if ($this->exception) {
|
||||
@ -127,10 +120,8 @@ final class Coroutine implements Promise, \ArrayAccess
|
||||
$this->resolve($yielded->getReturn());
|
||||
$this->onResolve = null;
|
||||
$this->generator->next();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$this->generator->valid()) {
|
||||
if (PHP_MAJOR_VERSION >= 7) {
|
||||
$this->resolve($this->generator->getReturn());
|
||||
@ -138,7 +129,6 @@ final class Coroutine implements Promise, \ArrayAccess
|
||||
$this->resolve(null);
|
||||
}
|
||||
$this->onResolve = null;
|
||||
|
||||
return;
|
||||
}
|
||||
if ($yielded instanceof \Generator) {
|
||||
@ -164,9 +154,8 @@ final class Coroutine implements Promise, \ArrayAccess
|
||||
};
|
||||
$yielded->onResolve($this->onResolve);
|
||||
}
|
||||
|
||||
/**
|
||||
* Throw exception into the generator
|
||||
* Throw exception into the generator.
|
||||
*
|
||||
* @param \Throwable $reason Exception
|
||||
*
|
||||
@ -177,7 +166,7 @@ final class Coroutine implements Promise, \ArrayAccess
|
||||
public function throw(\Throwable $reason)
|
||||
{
|
||||
if (!isset($reason->yieldedFrames)) {
|
||||
if (method_exists($reason, 'updateTLTrace')) {
|
||||
if (\method_exists($reason, 'updateTLTrace')) {
|
||||
$reason->updateTLTrace($this->getTrace());
|
||||
} else {
|
||||
$reason->yieldedFrames = $this->getTrace();
|
||||
@ -185,7 +174,6 @@ final class Coroutine implements Promise, \ArrayAccess
|
||||
}
|
||||
$this->generator->throw($reason);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Throwable $reason Failure reason.
|
||||
*/
|
||||
@ -193,7 +181,6 @@ final class Coroutine implements Promise, \ArrayAccess
|
||||
{
|
||||
$this->resolve(new Failure($reason));
|
||||
}
|
||||
|
||||
public function offsetExists($offset): bool
|
||||
{
|
||||
throw new Exception('Not supported!');
|
||||
@ -207,14 +194,14 @@ final class Coroutine implements Promise, \ArrayAccess
|
||||
*/
|
||||
public function offsetGet($offset)
|
||||
{
|
||||
return Tools::call((function () use ($offset) {
|
||||
return Tools::call((function () use ($offset): \Generator {
|
||||
$result = yield $this;
|
||||
return $result[$offset];
|
||||
})());
|
||||
}
|
||||
public function offsetSet($offset, $value)
|
||||
{
|
||||
return Tools::call((function () use ($offset, $value) {
|
||||
return Tools::call((function () use ($offset, $value): \Generator {
|
||||
$result = yield $this;
|
||||
if ($offset === null) {
|
||||
return $result[] = $value;
|
||||
@ -224,12 +211,11 @@ final class Coroutine implements Promise, \ArrayAccess
|
||||
}
|
||||
public function offsetUnset($offset)
|
||||
{
|
||||
return Tools::call((function () use ($offset) {
|
||||
return Tools::call((function () use ($offset): \Generator {
|
||||
$result = yield $this;
|
||||
unset($result[$offset]);
|
||||
})());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an attribute asynchronously.
|
||||
*
|
||||
@ -239,19 +225,18 @@ final class Coroutine implements Promise, \ArrayAccess
|
||||
*/
|
||||
public function __get(string $offset)
|
||||
{
|
||||
return Tools::call((function () use ($offset) {
|
||||
return Tools::call((function () use ($offset): \Generator {
|
||||
$result = yield $this;
|
||||
return $result->{$offset};
|
||||
})());
|
||||
}
|
||||
public function __call(string $name, array $arguments)
|
||||
{
|
||||
return Tools::call((function () use ($name, $arguments) {
|
||||
return Tools::call((function () use ($name, $arguments): \Generator {
|
||||
$result = yield $this;
|
||||
return $result->{$name}($arguments);
|
||||
})());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current stack trace for running coroutine.
|
||||
*
|
||||
@ -263,13 +248,7 @@ final class Coroutine implements Promise, \ArrayAccess
|
||||
try {
|
||||
$reflector = new ReflectionGenerator($this->generator);
|
||||
$frames = $reflector->getTrace();
|
||||
$frames []= \array_merge(
|
||||
$this->parentCoroutine ? $this->parentCoroutine->getFrame() : [],
|
||||
[
|
||||
'function' => $reflector->getFunction()->getName(),
|
||||
'args' => []
|
||||
]
|
||||
);
|
||||
$frames[] = \array_merge($this->parentCoroutine ? $this->parentCoroutine->getFrame() : [], ['function' => $reflector->getFunction()->getName(), 'args' => []]);
|
||||
} catch (\Throwable $e) {
|
||||
}
|
||||
if ($this->parentCoroutine) {
|
||||
@ -277,9 +256,8 @@ final class Coroutine implements Promise, \ArrayAccess
|
||||
}
|
||||
return $frames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current execution frame
|
||||
* Get current execution frame.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
@ -287,10 +265,7 @@ final class Coroutine implements Promise, \ArrayAccess
|
||||
{
|
||||
try {
|
||||
$reflector = new ReflectionGenerator($this->generator);
|
||||
return [
|
||||
'file' => $reflector->getExecutingFile(),
|
||||
'line' => $reflector->getExecutingLine(),
|
||||
];
|
||||
return ['file' => $reflector->getExecutingFile(), 'line' => $reflector->getExecutingLine()];
|
||||
} catch (\Throwable $e) {
|
||||
}
|
||||
return [];
|
||||
|
@ -114,12 +114,10 @@ class DataCenter
|
||||
* @var \Amp\Http\Client\Cookie\CookieJar
|
||||
*/
|
||||
private $CookieJar;
|
||||
|
||||
public function __sleep()
|
||||
{
|
||||
return ['sockets', 'curdc', 'dclist', 'settings'];
|
||||
}
|
||||
|
||||
public function __wakeup()
|
||||
{
|
||||
$array = [];
|
||||
@ -136,7 +134,6 @@ class DataCenter
|
||||
}
|
||||
$this->setDataCenterConnections($array);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set auth key information from saved auth array.
|
||||
*
|
||||
@ -147,7 +144,7 @@ class DataCenter
|
||||
public function setDataCenterConnections(array $saved)
|
||||
{
|
||||
foreach ($saved as $id => $data) {
|
||||
$connection = $this->sockets[$id] = new DataCenterConnection;
|
||||
$connection = $this->sockets[$id] = new DataCenterConnection();
|
||||
if (isset($data['permAuthKey'])) {
|
||||
$connection->setPermAuthKey(new PermAuthKey($data['permAuthKey']));
|
||||
}
|
||||
@ -187,12 +184,11 @@ class DataCenter
|
||||
public function __magic_construct($API, array $dclist, array $settings, bool $reconnectAll = true, CookieJar $jar = null)
|
||||
{
|
||||
$this->API = $API;
|
||||
|
||||
$changed = [];
|
||||
$changedSettings = $this->settings !== $settings;
|
||||
if (!$reconnectAll) {
|
||||
$changed = [];
|
||||
$test = ($API->getCachedConfig()['test_mode'] ?? false) ? 'test' : 'main';
|
||||
$test = $API->getCachedConfig()['test_mode'] ?? false ? 'test' : 'main';
|
||||
foreach ($dclist[$test] as $ipv6 => $dcs) {
|
||||
foreach ($dcs as $id => $dc) {
|
||||
if ($dc !== ($this->dclist[$test][$ipv6][$id] ?? [])) {
|
||||
@ -201,7 +197,6 @@ class DataCenter
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->dclist = $dclist;
|
||||
$this->settings = $settings;
|
||||
foreach ($this->sockets as $key => $socket) {
|
||||
@ -217,75 +212,40 @@ class DataCenter
|
||||
unset($this->sockets[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
if ($reconnectAll || $changedSettings || !$this->CookieJar) {
|
||||
$this->CookieJar = $jar ?? new InMemoryCookieJar;
|
||||
$this->HTTPClient = (new HttpClientBuilder)
|
||||
->interceptNetwork(new CookieInterceptor($this->CookieJar))
|
||||
->usingPool(new UnlimitedConnectionPool(new DefaultConnectionFactory(new ContextConnector($this))))
|
||||
->build();
|
||||
|
||||
$DoHHTTPClient = (new HttpClientBuilder)
|
||||
->interceptNetwork(new CookieInterceptor($this->CookieJar))
|
||||
->usingPool(new UnlimitedConnectionPool(new DefaultConnectionFactory(new ContextConnector($this, true))))
|
||||
->build();
|
||||
|
||||
$DoHConfig = new DoHConfig(
|
||||
[
|
||||
new Nameserver('https://mozilla.cloudflare-dns.com/dns-query'),
|
||||
new Nameserver('https://dns.google/resolve'),
|
||||
],
|
||||
$DoHHTTPClient
|
||||
);
|
||||
$nonProxiedDoHConfig = new DoHConfig(
|
||||
[
|
||||
new Nameserver('https://mozilla.cloudflare-dns.com/dns-query'),
|
||||
new Nameserver('https://dns.google/resolve'),
|
||||
]
|
||||
);
|
||||
$this->DoHClient = Magic::$altervista || Magic::$zerowebhost ?
|
||||
new Rfc1035StubResolver() :
|
||||
new Rfc8484StubResolver($DoHConfig);
|
||||
|
||||
$this->nonProxiedDoHClient = Magic::$altervista || Magic::$zerowebhost ?
|
||||
new Rfc1035StubResolver() :
|
||||
new Rfc8484StubResolver($nonProxiedDoHConfig);
|
||||
$this->CookieJar = $jar ?? new InMemoryCookieJar();
|
||||
$this->HTTPClient = (new HttpClientBuilder())->interceptNetwork(new CookieInterceptor($this->CookieJar))->usingPool(new UnlimitedConnectionPool(new DefaultConnectionFactory(new ContextConnector($this))))->build();
|
||||
$DoHHTTPClient = (new HttpClientBuilder())->interceptNetwork(new CookieInterceptor($this->CookieJar))->usingPool(new UnlimitedConnectionPool(new DefaultConnectionFactory(new ContextConnector($this, true))))->build();
|
||||
$DoHConfig = new DoHConfig([new Nameserver('https://mozilla.cloudflare-dns.com/dns-query'), new Nameserver('https://dns.google/resolve')], $DoHHTTPClient);
|
||||
$nonProxiedDoHConfig = new DoHConfig([new Nameserver('https://mozilla.cloudflare-dns.com/dns-query'), new Nameserver('https://dns.google/resolve')]);
|
||||
$this->DoHClient = Magic::$altervista || Magic::$zerowebhost ? new Rfc1035StubResolver() : new Rfc8484StubResolver($DoHConfig);
|
||||
$this->nonProxiedDoHClient = Magic::$altervista || Magic::$zerowebhost ? new Rfc1035StubResolver() : new Rfc8484StubResolver($nonProxiedDoHConfig);
|
||||
}
|
||||
}
|
||||
|
||||
public function dcConnect(string $dc_number, int $id = -1): \Generator
|
||||
{
|
||||
$old = isset($this->sockets[$dc_number]) && (
|
||||
$this->sockets[$dc_number]->shouldReconnect() ||
|
||||
(
|
||||
$id !== -1
|
||||
&& $this->sockets[$dc_number]->hasConnection($id)
|
||||
&& $this->sockets[$dc_number]->getConnection($id)->shouldReconnect()
|
||||
)
|
||||
);
|
||||
$old = isset($this->sockets[$dc_number]) && ($this->sockets[$dc_number]->shouldReconnect() || $id !== -1 && $this->sockets[$dc_number]->hasConnection($id) && $this->sockets[$dc_number]->getConnection($id)->shouldReconnect());
|
||||
if (isset($this->sockets[$dc_number]) && !$old) {
|
||||
$this->API->logger("Not reconnecting to DC $dc_number ($id)");
|
||||
$this->API->logger("Not reconnecting to DC {$dc_number} ({$id})");
|
||||
return false;
|
||||
}
|
||||
$ctxs = $this->generateContexts($dc_number);
|
||||
|
||||
if (empty($ctxs)) {
|
||||
return false;
|
||||
}
|
||||
foreach ($ctxs as $ctx) {
|
||||
try {
|
||||
if ($old) {
|
||||
$this->API->logger->logger("Reconnecting to DC $dc_number ($id) from existing", \danog\MadelineProto\Logger::WARNING);
|
||||
$this->API->logger->logger("Reconnecting to DC {$dc_number} ({$id}) from existing", \danog\MadelineProto\Logger::WARNING);
|
||||
$this->sockets[$dc_number]->setExtra($this->API);
|
||||
yield $this->sockets[$dc_number]->connect($ctx, $id);
|
||||
} else {
|
||||
$this->API->logger->logger("Connecting to DC $dc_number from scratch", \danog\MadelineProto\Logger::WARNING);
|
||||
$this->API->logger->logger("Connecting to DC {$dc_number} from scratch", \danog\MadelineProto\Logger::WARNING);
|
||||
$this->sockets[$dc_number] = new DataCenterConnection();
|
||||
$this->sockets[$dc_number]->setExtra($this->API);
|
||||
yield $this->sockets[$dc_number]->connect($ctx);
|
||||
}
|
||||
$this->API->logger->logger('OK!', \danog\MadelineProto\Logger::WARNING);
|
||||
|
||||
return true;
|
||||
} catch (\Throwable $e) {
|
||||
if (\MADELINEPROTO_TEST === 'pony') {
|
||||
@ -294,19 +254,15 @@ class DataCenter
|
||||
$this->API->logger->logger('Connection failed: ' . $e->getMessage(), \danog\MadelineProto\Logger::ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
throw new \danog\MadelineProto\Exception("Could not connect to DC $dc_number");
|
||||
throw new \danog\MadelineProto\Exception("Could not connect to DC {$dc_number}");
|
||||
}
|
||||
|
||||
public function generateContexts($dc_number = 0, string $uri = '', ConnectContext $context = null)
|
||||
{
|
||||
$ctxs = [];
|
||||
$combos = [];
|
||||
|
||||
$dc_config_number = isset($this->settings[$dc_number]) ? $dc_number : 'all';
|
||||
$test = $this->settings[$dc_config_number]['test_mode'] ? 'test' : 'main';
|
||||
$ipv6 = $this->settings[$dc_config_number]['ipv6'] ? 'ipv6' : 'ipv4';
|
||||
|
||||
switch ($this->settings[$dc_config_number]['protocol']) {
|
||||
case 'abridged':
|
||||
case 'tcp_abridged':
|
||||
@ -359,13 +315,11 @@ class DataCenter
|
||||
$default = [[DefaultStream::getName(), []], [BufferedRawStream::getName(), []]];
|
||||
}
|
||||
$combos[] = $default;
|
||||
|
||||
if (!isset($this->settings[$dc_config_number]['do_not_retry'])) {
|
||||
if ((isset($this->dclist[$test][$ipv6][$dc_number]['tcpo_only']) && $this->dclist[$test][$ipv6][$dc_number]['tcpo_only']) || isset($this->dclist[$test][$ipv6][$dc_number]['secret'])) {
|
||||
if (isset($this->dclist[$test][$ipv6][$dc_number]['tcpo_only']) && $this->dclist[$test][$ipv6][$dc_number]['tcpo_only'] || isset($this->dclist[$test][$ipv6][$dc_number]['secret'])) {
|
||||
$extra = isset($this->dclist[$test][$ipv6][$dc_number]['secret']) ? ['secret' => $this->dclist[$test][$ipv6][$dc_number]['secret']] : [];
|
||||
$combos[] = [[DefaultStream::getName(), []], [BufferedRawStream::getName(), []], [ObfuscatedStream::getName(), $extra], [IntermediatePaddedStream::getName(), []]];
|
||||
}
|
||||
|
||||
if (\is_iterable($this->settings[$dc_config_number]['proxy'])) {
|
||||
$proxies = $this->settings[$dc_config_number]['proxy'];
|
||||
$proxy_extras = $this->settings[$dc_config_number]['proxy_extra'];
|
||||
@ -423,12 +377,10 @@ class DataCenter
|
||||
$combo = \array_merge($first, $second);
|
||||
}
|
||||
}
|
||||
|
||||
\array_unshift($combos, $combo);
|
||||
//unset($combos[$k]);
|
||||
}
|
||||
}
|
||||
|
||||
if ($dc_number) {
|
||||
$combos[] = [[DefaultStream::getName(), []], [BufferedRawStream::getName(), []], [HttpsStream::getName(), []]];
|
||||
}
|
||||
@ -436,19 +388,13 @@ class DataCenter
|
||||
}
|
||||
/* @var $context \Amp\ConnectContext */
|
||||
$context = $context ?? (new ConnectContext())->withMaxAttempts(1)->withConnectTimeout(1000 * $this->settings[$dc_config_number]['timeout']);
|
||||
|
||||
foreach ($combos as $combo) {
|
||||
$ipv6 = [$this->settings[$dc_config_number]['ipv6'] ? 'ipv6' : 'ipv4', $this->settings[$dc_config_number]['ipv6'] ? 'ipv4' : 'ipv6'];
|
||||
|
||||
foreach ($ipv6 as $ipv6) {
|
||||
// This is only for non-MTProto connections
|
||||
if (!$dc_number) {
|
||||
/* @var $ctx \danog\MadelineProto\Stream\ConnectionContext */
|
||||
$ctx = (new ConnectionContext())
|
||||
->setSocketContext($context)
|
||||
->setUri($uri)
|
||||
->setIpv6($ipv6 === 'ipv6');
|
||||
|
||||
$ctx = (new ConnectionContext())->setSocketContext($context)->setUri($uri)->setIpv6($ipv6 === 'ipv6');
|
||||
foreach ($combo as $stream) {
|
||||
if ($stream[0] === DefaultStream::getName() && $stream[1] === []) {
|
||||
$stream[1] = new DoHConnector($this, $ctx);
|
||||
@ -458,59 +404,44 @@ class DataCenter
|
||||
$ctxs[] = $ctx;
|
||||
continue;
|
||||
}
|
||||
|
||||
// This is only for MTProto connections
|
||||
if (!isset($this->dclist[$test][$ipv6][$dc_number]['ip_address'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$address = $this->dclist[$test][$ipv6][$dc_number]['ip_address'];
|
||||
$port = $this->dclist[$test][$ipv6][$dc_number]['port'];
|
||||
|
||||
foreach (\array_unique([$port, 443, 80, 88, 5222]) as $port) {
|
||||
$stream = \end($combo)[0];
|
||||
|
||||
if ($stream === HttpsStream::getName()) {
|
||||
$subdomain = $this->dclist['ssl_subdomains'][\preg_replace('/\D+/', '', $dc_number)];
|
||||
$subdomain = $this->dclist['ssl_subdomains'][\preg_replace('/\\D+/', '', $dc_number)];
|
||||
if (\strpos($dc_number, '_media') !== false) {
|
||||
$subdomain .= '-1';
|
||||
}
|
||||
$path = $this->settings[$dc_config_number]['test_mode'] ? 'apiw_test1' : 'apiw1';
|
||||
|
||||
$uri = 'tcp://' . $subdomain . '.web.telegram.org:' . $port . '/' . $path;
|
||||
} elseif ($stream === HttpStream::getName()) {
|
||||
$uri = 'tcp://' . $address . ':' . $port . '/api';
|
||||
} else {
|
||||
$uri = 'tcp://' . $address . ':' . $port;
|
||||
}
|
||||
|
||||
if ($combo[1][0] === WssStream::getName()) {
|
||||
$subdomain = $this->dclist['ssl_subdomains'][\preg_replace('/\D+/', '', $dc_number)];
|
||||
$subdomain = $this->dclist['ssl_subdomains'][\preg_replace('/\\D+/', '', $dc_number)];
|
||||
if (\strpos($dc_number, '_media') !== false) {
|
||||
$subdomain .= '-1';
|
||||
}
|
||||
$path = $this->settings[$dc_config_number]['test_mode'] ? 'apiws_test' : 'apiws';
|
||||
|
||||
$uri = 'tcp://' . $subdomain . '.web.telegram.org:' . $port . '/' . $path;
|
||||
} elseif ($combo[1][0] === WsStream::getName()) {
|
||||
$subdomain = $this->dclist['ssl_subdomains'][\preg_replace('/\D+/', '', $dc_number)];
|
||||
$subdomain = $this->dclist['ssl_subdomains'][\preg_replace('/\\D+/', '', $dc_number)];
|
||||
if (\strpos($dc_number, '_media') !== false) {
|
||||
$subdomain .= '-1';
|
||||
}
|
||||
$path = $this->settings[$dc_config_number]['test_mode'] ? 'apiws_test' : 'apiws';
|
||||
|
||||
//$uri = 'tcp://' . $subdomain . '.web.telegram.org:' . $port . '/' . $path;
|
||||
$uri = 'tcp://' . $address . ':' . $port . '/' . $path;
|
||||
}
|
||||
|
||||
/* @var $ctx \danog\MadelineProto\Stream\ConnectionContext */
|
||||
$ctx = (new ConnectionContext())
|
||||
->setDc($dc_number)
|
||||
->setTest($this->settings[$dc_config_number]['test_mode'])
|
||||
->setSocketContext($context)
|
||||
->setUri($uri)
|
||||
->setIpv6($ipv6 === 'ipv6');
|
||||
|
||||
$ctx = (new ConnectionContext())->setDc($dc_number)->setTest($this->settings[$dc_config_number]['test_mode'])->setSocketContext($context)->setUri($uri)->setIpv6($ipv6 === 'ipv6');
|
||||
foreach ($combo as $stream) {
|
||||
if ($stream[0] === DefaultStream::getName() && $stream[1] === []) {
|
||||
$stream[1] = new DoHConnector($this, $ctx);
|
||||
@ -524,22 +455,17 @@ class DataCenter
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($this->dclist[$test][$ipv6][$dc_number . '_bk']['ip_address'])) {
|
||||
$ctxs = \array_merge($ctxs, $this->generateContexts($dc_number . '_bk'));
|
||||
}
|
||||
|
||||
if (empty($ctxs)) {
|
||||
unset($this->sockets[$dc_number]);
|
||||
|
||||
$this->API->logger->logger("No info for DC $dc_number", \danog\MadelineProto\Logger::ERROR);
|
||||
$this->API->logger->logger("No info for DC {$dc_number}", \danog\MadelineProto\Logger::ERROR);
|
||||
} elseif (\MADELINEPROTO_TEST === 'pony') {
|
||||
return [$ctxs[0]];
|
||||
}
|
||||
|
||||
return $ctxs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get main API.
|
||||
*
|
||||
@ -549,7 +475,6 @@ class DataCenter
|
||||
{
|
||||
return $this->API;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get async HTTP client.
|
||||
*
|
||||
@ -559,7 +484,6 @@ class DataCenter
|
||||
{
|
||||
return $this->HTTPClient;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get async HTTP client cookies.
|
||||
*
|
||||
@ -587,7 +511,6 @@ class DataCenter
|
||||
{
|
||||
return $this->nonProxiedDoHClient;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get contents of file.
|
||||
*
|
||||
@ -599,7 +522,6 @@ class DataCenter
|
||||
{
|
||||
return yield (yield $this->getHTTPClient()->request(new Request($url)))->getBody()->buffer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Connection instance for authorization.
|
||||
*
|
||||
@ -664,8 +586,6 @@ class DataCenter
|
||||
{
|
||||
return isset($this->sockets[$dc]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if connected to datacenter using HTTP.
|
||||
*
|
||||
@ -677,7 +597,6 @@ class DataCenter
|
||||
{
|
||||
return $this->sockets[$datacenter]->isHttp();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if connected to datacenter directly using IP address.
|
||||
*
|
||||
@ -689,7 +608,6 @@ class DataCenter
|
||||
{
|
||||
return $this->sockets[$datacenter]->byIPAddress();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all DC IDs.
|
||||
*
|
||||
@ -701,7 +619,6 @@ class DataCenter
|
||||
{
|
||||
$test = $this->settings['all']['test_mode'] ? 'test' : 'main';
|
||||
$ipv6 = $this->settings['all']['ipv6'] ? 'ipv6' : 'ipv4';
|
||||
|
||||
return $all ? \array_keys((array) $this->dclist[$test][$ipv6]) : \array_keys((array) $this->sockets);
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Connection module handling all connections to a datacenter.
|
||||
*
|
||||
@ -36,7 +37,6 @@ class DataCenterConnection implements JsonSerializable
|
||||
const READ_WEIGHT = 1;
|
||||
const READ_WEIGHT_MEDIA = 5;
|
||||
const WRITE_WEIGHT = 10;
|
||||
|
||||
/**
|
||||
* Promise for connection.
|
||||
*
|
||||
@ -49,7 +49,6 @@ class DataCenterConnection implements JsonSerializable
|
||||
* @var Deferred
|
||||
*/
|
||||
private $connectionsDeferred;
|
||||
|
||||
/**
|
||||
* Temporary auth key.
|
||||
*
|
||||
@ -62,7 +61,6 @@ class DataCenterConnection implements JsonSerializable
|
||||
* @var PermAuthKey|null
|
||||
*/
|
||||
private $permAuthKey;
|
||||
|
||||
/**
|
||||
* Connections open to a certain DC.
|
||||
*
|
||||
@ -75,42 +73,36 @@ class DataCenterConnection implements JsonSerializable
|
||||
* @var array<string, int>
|
||||
*/
|
||||
private $availableConnections = [];
|
||||
|
||||
/**
|
||||
* Main API instance.
|
||||
*
|
||||
* @var \danog\MadelineProto\MTProto
|
||||
*/
|
||||
private $API;
|
||||
|
||||
/**
|
||||
* Connection context.
|
||||
*
|
||||
* @var ConnectionContext
|
||||
*/
|
||||
private $ctx;
|
||||
|
||||
/**
|
||||
* DC ID.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $datacenter;
|
||||
|
||||
/**
|
||||
* Linked DC ID.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $linked;
|
||||
|
||||
/**
|
||||
* Loop to keep weights at sane value.
|
||||
*
|
||||
* @var \danog\MadelineProto\Loop\Generic\PeriodicLoop
|
||||
*/
|
||||
private $robinLoop;
|
||||
|
||||
/**
|
||||
* Decrement roundrobin weight by this value if busy reading.
|
||||
*
|
||||
@ -123,14 +115,12 @@ class DataCenterConnection implements JsonSerializable
|
||||
* @var integer
|
||||
*/
|
||||
private $decWrite = 10;
|
||||
|
||||
/**
|
||||
* Backed up messages.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $backup = [];
|
||||
|
||||
/**
|
||||
* Whether this socket has to be reconnected.
|
||||
*
|
||||
@ -191,7 +181,6 @@ class DataCenterConnection implements JsonSerializable
|
||||
{
|
||||
$this->{$temp ? 'tempAuthKey' : 'permAuthKey'} = $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get temporary authorization key.
|
||||
*
|
||||
@ -210,7 +199,6 @@ class DataCenterConnection implements JsonSerializable
|
||||
{
|
||||
return $this->getAuthKey(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if has temporary authorization key.
|
||||
*
|
||||
@ -220,7 +208,6 @@ class DataCenterConnection implements JsonSerializable
|
||||
{
|
||||
return $this->hasAuthKey(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if has permanent authorization key.
|
||||
*
|
||||
@ -230,7 +217,6 @@ class DataCenterConnection implements JsonSerializable
|
||||
{
|
||||
return $this->hasAuthKey(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set temporary authorization key.
|
||||
*
|
||||
@ -253,7 +239,6 @@ class DataCenterConnection implements JsonSerializable
|
||||
{
|
||||
return $this->setAuthKey($key, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind temporary and permanent auth keys.
|
||||
*
|
||||
@ -286,7 +271,6 @@ class DataCenterConnection implements JsonSerializable
|
||||
{
|
||||
return $this->hasTempAuthKey() ? $this->getTempAuthKey()->isAuthorized() : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the authorized boolean.
|
||||
*
|
||||
@ -302,7 +286,6 @@ class DataCenterConnection implements JsonSerializable
|
||||
$this->getTempAuthKey()->authorized($authorized);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Link permanent authorization info of main DC to media DC.
|
||||
*
|
||||
@ -315,7 +298,6 @@ class DataCenterConnection implements JsonSerializable
|
||||
$this->linked = $dc;
|
||||
$this->permAuthKey =& $this->API->datacenter->getDataCenterConnection($dc)->permAuthKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset MTProto sessions.
|
||||
*
|
||||
@ -349,7 +331,6 @@ class DataCenterConnection implements JsonSerializable
|
||||
$socket->flush();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get connection context.
|
||||
*
|
||||
@ -359,7 +340,6 @@ class DataCenterConnection implements JsonSerializable
|
||||
{
|
||||
return $this->ctx;
|
||||
}
|
||||
|
||||
/**
|
||||
* Has connection context?
|
||||
*
|
||||
@ -369,7 +349,6 @@ class DataCenterConnection implements JsonSerializable
|
||||
{
|
||||
return isset($this->ctx);
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect function.
|
||||
*
|
||||
@ -380,32 +359,26 @@ class DataCenterConnection implements JsonSerializable
|
||||
*/
|
||||
public function connect(ConnectionContext $ctx, int $id = -1): \Generator
|
||||
{
|
||||
$this->API->logger->logger("Trying shared connection via $ctx ($id)");
|
||||
|
||||
$this->API->logger->logger("Trying shared connection via {$ctx} ({$id})");
|
||||
$this->ctx = $ctx->getCtx();
|
||||
$this->datacenter = $ctx->getDc();
|
||||
$media = $ctx->isMedia() || $ctx->isCDN();
|
||||
|
||||
$count = $media ? $this->API->settings['connection_settings']['media_socket_count']['min'] : 1;
|
||||
|
||||
if ($count > 1) {
|
||||
if (!$this->robinLoop) {
|
||||
$this->robinLoop = new PeriodicLoop($this->API, [$this, 'even'], "robin loop DC {$this->datacenter}", $this->API->settings['connection_settings']['robin_period']);
|
||||
}
|
||||
$this->robinLoop->start();
|
||||
}
|
||||
|
||||
$this->decRead = $media ? self::READ_WEIGHT_MEDIA : self::READ_WEIGHT;
|
||||
$this->decWrite = self::WRITE_WEIGHT;
|
||||
|
||||
if ($id === -1 || !isset($this->connections[$id])) {
|
||||
if ($this->connections) {
|
||||
$this->API->logger("Already connected!", Logger::WARNING);
|
||||
return;
|
||||
}
|
||||
yield $this->connectMore($count);
|
||||
yield from $this->connectMore($count);
|
||||
yield $this->restoreBackup();
|
||||
|
||||
$this->connectionsPromise = new Success();
|
||||
if ($this->connectionsDeferred) {
|
||||
$connectionsDeferred = $this->connectionsDeferred;
|
||||
@ -414,10 +387,9 @@ class DataCenterConnection implements JsonSerializable
|
||||
}
|
||||
} else {
|
||||
$this->availableConnections[$id] = 0;
|
||||
yield $this->connections[$id]->connect($ctx);
|
||||
yield from $this->connections[$id]->connect($ctx);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect to the DC using count more sockets.
|
||||
*
|
||||
@ -425,21 +397,19 @@ class DataCenterConnection implements JsonSerializable
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function connectMore(int $count)
|
||||
private function connectMore(int $count): \Generator
|
||||
{
|
||||
$ctx = $this->ctx->getCtx();
|
||||
$count += $previousCount = \count($this->connections);
|
||||
for ($x = $previousCount; $x < $count; $x++) {
|
||||
$connection = new Connection();
|
||||
$connection->setExtra($this, $x);
|
||||
yield $connection->connect($ctx);
|
||||
|
||||
yield from $connection->connect($ctx);
|
||||
$this->connections[$x] = $connection;
|
||||
$this->availableConnections[$x] = 0;
|
||||
$ctx = $this->ctx->getCtx();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Signal that a connection ID disconnected.
|
||||
*
|
||||
@ -459,12 +429,10 @@ class DataCenterConnection implements JsonSerializable
|
||||
$list .= $message['_'] ?? '-';
|
||||
$list .= ', ';
|
||||
}
|
||||
$this->API->logger->logger("Backed up $list from DC {$this->datacenter}.$id");
|
||||
$this->API->logger->logger("Backed up {$list} from DC {$this->datacenter}.{$id}");
|
||||
$this->backup = \array_merge($this->backup, $backup);
|
||||
|
||||
unset($this->connections[$id], $this->availableConnections[$id]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Close all connections to DC.
|
||||
*
|
||||
@ -472,9 +440,8 @@ class DataCenterConnection implements JsonSerializable
|
||||
*/
|
||||
public function disconnect()
|
||||
{
|
||||
$this->connectionsDeferred = new Deferred;
|
||||
$this->connectionsDeferred = new Deferred();
|
||||
$this->connectionsPromise = $this->connectionsDeferred->promise();
|
||||
|
||||
$this->API->logger->logger("Disconnecting from shared DC {$this->datacenter}");
|
||||
if ($this->robinLoop) {
|
||||
$this->robinLoop->signal(true);
|
||||
@ -485,12 +452,10 @@ class DataCenterConnection implements JsonSerializable
|
||||
$connection->disconnect();
|
||||
}
|
||||
$count = \count($this->backup) - $before;
|
||||
$this->API->logger->logger("Backed up $count, added to $before existing messages) from DC {$this->datacenter}");
|
||||
|
||||
$this->API->logger->logger("Backed up {$count}, added to {$before} existing messages) from DC {$this->datacenter}");
|
||||
$this->connections = [];
|
||||
$this->availableConnections = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Reconnect to DC.
|
||||
*
|
||||
@ -500,9 +465,8 @@ class DataCenterConnection implements JsonSerializable
|
||||
{
|
||||
$this->API->logger->logger("Reconnecting shared DC {$this->datacenter}");
|
||||
$this->disconnect();
|
||||
yield $this->connect($this->ctx);
|
||||
yield from $this->connect($this->ctx);
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore backed up messages.
|
||||
*
|
||||
@ -513,7 +477,7 @@ class DataCenterConnection implements JsonSerializable
|
||||
$backup = $this->backup;
|
||||
$this->backup = [];
|
||||
$count = \count($backup);
|
||||
$this->API->logger->logger("Restoring $count messages to DC {$this->datacenter}");
|
||||
$this->API->logger->logger("Restoring {$count} messages to DC {$this->datacenter}");
|
||||
foreach ($backup as $message) {
|
||||
Tools::callFork($this->getConnection()->sendMessage($message, false));
|
||||
}
|
||||
@ -528,7 +492,6 @@ class DataCenterConnection implements JsonSerializable
|
||||
{
|
||||
return $this->connections[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if any connection is available.
|
||||
*
|
||||
@ -548,12 +511,10 @@ class DataCenterConnection implements JsonSerializable
|
||||
public function waitGetConnection(): Promise
|
||||
{
|
||||
if (empty($this->availableConnections)) {
|
||||
$deferred = new Deferred;
|
||||
$this->connectionsPromise->onResolve(
|
||||
function ($e, $v) use ($deferred) {
|
||||
$deferred = new Deferred();
|
||||
$this->connectionsPromise->onResolve(function ($e, $v) use ($deferred) {
|
||||
$deferred->resolve($this->getConnection());
|
||||
}
|
||||
);
|
||||
});
|
||||
return $deferred->promise();
|
||||
}
|
||||
return new Success($this->getConnection());
|
||||
@ -575,12 +536,10 @@ class DataCenterConnection implements JsonSerializable
|
||||
}
|
||||
$max = \max($this->availableConnections);
|
||||
$key = \array_search($max, $this->availableConnections);
|
||||
|
||||
// Decrease to implement round robin
|
||||
$this->availableConnections[$key]--;
|
||||
return $this->connections[$key];
|
||||
}
|
||||
|
||||
/**
|
||||
* Even out round robin values.
|
||||
*
|
||||
@ -607,7 +566,6 @@ class DataCenterConnection implements JsonSerializable
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that one of the sockets is busy reading.
|
||||
*
|
||||
@ -632,8 +590,6 @@ class DataCenterConnection implements JsonSerializable
|
||||
{
|
||||
$this->availableConnections[$x] += $writing ? -$this->decWrite : $this->decWrite;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set main instance.
|
||||
*
|
||||
@ -645,7 +601,6 @@ class DataCenterConnection implements JsonSerializable
|
||||
{
|
||||
$this->API = $API;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get main instance.
|
||||
*
|
||||
@ -655,7 +610,6 @@ class DataCenterConnection implements JsonSerializable
|
||||
{
|
||||
return $this->API;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if is an HTTP connection.
|
||||
*
|
||||
@ -665,7 +619,6 @@ class DataCenterConnection implements JsonSerializable
|
||||
{
|
||||
return \in_array($this->ctx->getStreamName(), [HttpStream::getName(), HttpsStream::getName()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if is connected directly by IP address.
|
||||
*
|
||||
@ -675,7 +628,6 @@ class DataCenterConnection implements JsonSerializable
|
||||
{
|
||||
return !$this->ctx->hasStreamName(WssStream::getName()) && !$this->ctx->hasStreamName(HttpsStream::getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if is a media connection.
|
||||
*
|
||||
@ -685,7 +637,6 @@ class DataCenterConnection implements JsonSerializable
|
||||
{
|
||||
return $this->ctx->isMedia();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if is a CDN connection.
|
||||
*
|
||||
@ -695,7 +646,6 @@ class DataCenterConnection implements JsonSerializable
|
||||
{
|
||||
return $this->ctx->isCDN();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get DC-specific settings.
|
||||
*
|
||||
@ -706,7 +656,6 @@ class DataCenterConnection implements JsonSerializable
|
||||
$dc_config_number = isset($this->API->settings['connection_settings'][$this->datacenter]) ? $this->datacenter : 'all';
|
||||
return $this->API->settings['connection_settings'][$dc_config_number];
|
||||
}
|
||||
|
||||
/**
|
||||
* JSON serialize function.
|
||||
*
|
||||
@ -714,15 +663,7 @@ class DataCenterConnection implements JsonSerializable
|
||||
*/
|
||||
public function jsonSerialize(): array
|
||||
{
|
||||
return $this->linked ?
|
||||
[
|
||||
'linked' => $this->linked,
|
||||
'tempAuthKey' => $this->tempAuthKey
|
||||
] :
|
||||
[
|
||||
'permAuthKey' => $this->permAuthKey,
|
||||
'tempAuthKey' => $this->tempAuthKey
|
||||
];
|
||||
return $this->linked ? ['linked' => $this->linked, 'tempAuthKey' => $this->tempAuthKey] : ['permAuthKey' => $this->permAuthKey, 'tempAuthKey' => $this->tempAuthKey];
|
||||
}
|
||||
/**
|
||||
* Sleep function.
|
||||
|
@ -31,7 +31,6 @@ use Amp\Socket\ConnectException;
|
||||
use Amp\Socket\Connector;
|
||||
use Amp\Socket\ResourceSocket;
|
||||
use danog\MadelineProto\Stream\ConnectionContext;
|
||||
|
||||
use function Amp\Socket\Internal\parseUri;
|
||||
|
||||
class DoHConnector implements Connector
|
||||
@ -53,13 +52,11 @@ class DoHConnector implements Connector
|
||||
$this->dataCenter = $dataCenter;
|
||||
$this->ctx = $ctx;
|
||||
}
|
||||
|
||||
public function connect(string $uri, ?ConnectContext $socketContext = null, ?CancellationToken $token = null): Promise
|
||||
{
|
||||
return Tools::call((function () use ($uri, $socketContext, $token) {
|
||||
$socketContext = $socketContext ?? new ConnectContext;
|
||||
$token = $token ?? new NullCancellationToken;
|
||||
|
||||
return Tools::call((function () use ($uri, $socketContext, $token): \Generator {
|
||||
$socketContext = $socketContext ?? new ConnectContext();
|
||||
$token = $token ?? new NullCancellationToken();
|
||||
$attempt = 0;
|
||||
$uris = [];
|
||||
$failures = [];
|
||||
@ -105,7 +102,6 @@ class DoHConnector implements Connector
|
||||
if ($this->ctx->getIpv6()) {
|
||||
$records = \array_reverse($records);
|
||||
}
|
||||
|
||||
foreach ($records as $record) {
|
||||
/** @var Record $record */
|
||||
if ($record->getType() === Record::AAAA) {
|
||||
@ -115,34 +111,23 @@ class DoHConnector implements Connector
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$flags = \STREAM_CLIENT_CONNECT | \STREAM_CLIENT_ASYNC_CONNECT;
|
||||
$timeout = $socketContext->getConnectTimeout();
|
||||
foreach ($uris as $builtUri) {
|
||||
try {
|
||||
$streamContext = \stream_context_create($socketContext->withoutTlsContext()->toStreamContextArray());
|
||||
if (!$socket = @\stream_socket_client($builtUri, $errno, $errstr, null, $flags, $streamContext)) {
|
||||
throw new ConnectException(\sprintf(
|
||||
'Connection to %s failed: [Error #%d] %s%s',
|
||||
$uri,
|
||||
$errno,
|
||||
$errstr,
|
||||
$failures ? '; previous attempts: ' . \implode($failures) : ''
|
||||
), $errno);
|
||||
if (!($socket = @\stream_socket_client($builtUri, $errno, $errstr, null, $flags, $streamContext))) {
|
||||
throw new ConnectException(\sprintf('Connection to %s failed: [Error #%d] %s%s', $uri, $errno, $errstr, $failures ? '; previous attempts: ' . \implode($failures) : ''), $errno);
|
||||
}
|
||||
\stream_set_blocking($socket, false);
|
||||
$deferred = new Deferred;
|
||||
$deferred = new Deferred();
|
||||
$watcher = Loop::onWritable($socket, [$deferred, 'resolve']);
|
||||
$id = $token->subscribe([$deferred, 'fail']);
|
||||
try {
|
||||
yield Promise\timeout($deferred->promise(), $timeout);
|
||||
} catch (TimeoutException $e) {
|
||||
throw new ConnectException(\sprintf(
|
||||
'Connecting to %s failed: timeout exceeded (%d ms)%s',
|
||||
$uri,
|
||||
$timeout,
|
||||
$failures ? '; previous attempts: ' . \implode($failures) : ''
|
||||
), 110); // See ETIMEDOUT in http://www.virtsync.com/c-error-codes-include-errno
|
||||
throw new ConnectException(\sprintf('Connecting to %s failed: timeout exceeded (%d ms)%s', $uri, $timeout, $failures ? '; previous attempts: ' . \implode($failures) : ''), 110);
|
||||
// See ETIMEDOUT in http://www.virtsync.com/c-error-codes-include-errno
|
||||
} finally {
|
||||
Loop::cancel($watcher);
|
||||
$token->unsubscribe($id);
|
||||
@ -150,26 +135,21 @@ class DoHConnector implements Connector
|
||||
// The following hack looks like the only way to detect connection refused errors with PHP's stream sockets.
|
||||
if (\stream_socket_get_name($socket, true) === false) {
|
||||
\fclose($socket);
|
||||
throw new ConnectException(\sprintf(
|
||||
'Connection to %s refused%s',
|
||||
$uri,
|
||||
$failures ? '; previous attempts: ' . \implode($failures) : ''
|
||||
), 111); // See ECONNREFUSED in http://www.virtsync.com/c-error-codes-include-errno
|
||||
throw new ConnectException(\sprintf('Connection to %s refused%s', $uri, $failures ? '; previous attempts: ' . \implode($failures) : ''), 111);
|
||||
// See ECONNREFUSED in http://www.virtsync.com/c-error-codes-include-errno
|
||||
}
|
||||
} catch (ConnectException $e) {
|
||||
// Includes only error codes used in this file, as error codes on other OS families might be different.
|
||||
// In fact, this might show a confusing error message on OS families that return 110 or 111 by itself.
|
||||
$knownReasons = [
|
||||
110 => 'connection timeout',
|
||||
111 => 'connection refused',
|
||||
];
|
||||
$knownReasons = [110 => 'connection timeout', 111 => 'connection refused'];
|
||||
$code = $e->getCode();
|
||||
$reason = $knownReasons[$code] ?? ('Error #' . $code);
|
||||
$reason = $knownReasons[$code] ?? 'Error #' . $code;
|
||||
if (++$attempt === $socketContext->getMaxAttempts()) {
|
||||
break;
|
||||
}
|
||||
$failures[] = "{$uri} ({$reason})";
|
||||
continue; // Could not connect to host, try next host in the list.
|
||||
continue;
|
||||
// Could not connect to host, try next host in the list.
|
||||
}
|
||||
return ResourceSocket::fromClientSocket($socket, $socketContext->getTlsContext());
|
||||
}
|
||||
|
@ -28,7 +28,6 @@ class DocsBuilder
|
||||
use \danog\MadelineProto\DocsBuilder\Constructors;
|
||||
use Tools;
|
||||
public $td = false;
|
||||
|
||||
public function __construct($logger, $settings)
|
||||
{
|
||||
$this->logger = $logger;
|
||||
@ -50,10 +49,8 @@ class DocsBuilder
|
||||
\chdir($this->settings['output_dir']);
|
||||
$this->index = $settings['readme'] ? 'README.md' : 'index.md';
|
||||
}
|
||||
|
||||
public $types = [];
|
||||
public $any = '*';
|
||||
|
||||
public function mkDocs()
|
||||
{
|
||||
\danog\MadelineProto\Logger::log('Generating documentation index...', \danog\MadelineProto\Logger::NOTICE);
|
||||
@ -111,8 +108,7 @@ image: https://docs.madelineproto.xyz/favicons/android-chrome-256x256.png
|
||||
}
|
||||
$description = isset($this->td_descriptions['types'][$otype]) ? $this->td_descriptions['types'][$otype] : 'constructors and methods of type ' . $type;
|
||||
$symFile = \str_replace('.', '_', $type);
|
||||
$redir = $symFile !== $type ? "\nredirect_from: /API_docs/types/$symFile.html" : '';
|
||||
|
||||
$redir = $symFile !== $type ? "\nredirect_from: /API_docs/types/{$symFile}.html" : '';
|
||||
$header = '---
|
||||
title: ' . $type . '
|
||||
description: constructors and methods of type ' . $type . '
|
||||
@ -132,23 +128,11 @@ image: https://docs.madelineproto.xyz/favicons/android-chrome-256x256.png'.$redi
|
||||
The following syntaxes can also be used:
|
||||
|
||||
```
|
||||
$'.$type." = '@username'; // Username
|
||||
|
||||
\$".$type." = 'me'; // The currently logged-in user
|
||||
|
||||
\$".$type.' = 44700; // bot API id (users)
|
||||
$' . $type . " = '@username'; // Username\n\n\$" . $type . " = 'me'; // The currently logged-in user\n\n\$" . $type . ' = 44700; // bot API id (users)
|
||||
$' . $type . ' = -492772765; // bot API id (chats)
|
||||
$' . $type . ' = -10038575794; // bot API id (channels)
|
||||
|
||||
$'.$type." = 'https://t.me/danogentili'; // t.me URLs
|
||||
\$".$type." = 'https://t.me/joinchat/asfln1-21fa_'; // t.me invite links
|
||||
|
||||
\$".$type." = 'user#44700'; // tg-cli style id (users)
|
||||
\$".$type." = 'chat#492772765'; // tg-cli style id (chats)
|
||||
\$".$type." = 'channel#38575794'; // tg-cli style id (channels)
|
||||
```
|
||||
|
||||
A [Chat](Chat.md), a [User](User.md), an [InputPeer](InputPeer.md), an [InputDialogPeer](InputDialogPeer.md), an [InputNotifyPeer](InputNotifyPeer.md), an [InputUser](InputUser.md), an [InputChannel](InputChannel.md), a [Peer](Peer.md), an [DialogPeer](DialogPeer.md), [NotifyPeer](NotifyPeer.md), or a [Chat](Chat.md) object can also be used.\n\n\n";
|
||||
$' . $type . " = 'https://t.me/danogentili'; // t.me URLs\n\$" . $type . " = 'https://t.me/joinchat/asfln1-21fa_'; // t.me invite links\n\n\$" . $type . " = 'user#44700'; // tg-cli style id (users)\n\$" . $type . " = 'chat#492772765'; // tg-cli style id (chats)\n\$" . $type . " = 'channel#38575794'; // tg-cli style id (channels)\n```\n\nA [Chat](Chat.md), a [User](User.md), an [InputPeer](InputPeer.md), an [InputDialogPeer](InputDialogPeer.md), an [InputNotifyPeer](InputNotifyPeer.md), an [InputUser](InputUser.md), an [InputChannel](InputChannel.md), a [Peer](Peer.md), an [DialogPeer](DialogPeer.md), [NotifyPeer](NotifyPeer.md), or a [Chat](Chat.md) object can also be used.\n\n\n";
|
||||
}
|
||||
if (\in_array($type, ['InputEncryptedChat'])) {
|
||||
$header .= 'You can directly provide the [Update](Update.md) or [EncryptedMessage](EncryptedMessage.md) object here, MadelineProto will automatically extract the destination chat id.
|
||||
@ -389,7 +373,7 @@ Easy as pie:
|
||||
|
||||
```
|
||||
$call->storage["pony"] = "fluttershy";
|
||||
\danog\MadelineProto\Logger::log($call->storage["pony"]); // fluttershy
|
||||
\\danog\\MadelineProto\\Logger::log($call->storage["pony"]); // fluttershy
|
||||
```
|
||||
|
||||
Note: when modifying this property, *never* overwrite the previous values. Always either modify the values of the array separately like showed above, or use array_merge.
|
||||
@ -452,7 +436,7 @@ image: https://docs.madelineproto.xyz/favicons/android-chrome-256x256.png
|
||||
## Type: bytes
|
||||
[Back to constructor index](index.md)
|
||||
|
||||
An object of type `\danog\MadelineProto\TL\Types\Bytes`.
|
||||
An object of type `\\danog\\MadelineProto\\TL\\Types\\Bytes`.
|
||||
When casted to string, turns into a string of bytes of variable length, with length smaller than or equal to 16777215.
|
||||
When JSON-serialized, turns into an array of the following format:
|
||||
```
|
||||
@ -604,7 +588,6 @@ Any json-encodable data.
|
||||
');
|
||||
\danog\MadelineProto\Logger::log('Done!', \danog\MadelineProto\Logger::NOTICE);
|
||||
}
|
||||
|
||||
public static $template = '<?php
|
||||
/**
|
||||
* Lang module
|
||||
@ -622,7 +605,7 @@ Any json-encodable data.
|
||||
* @link https://docs.madelineproto.xyz MadelineProto documentation
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto;
|
||||
namespace danog\\MadelineProto;
|
||||
|
||||
class Lang
|
||||
{
|
||||
@ -631,7 +614,6 @@ class Lang
|
||||
// THIS WILL BE OVERWRITTEN BY $lang["en"]
|
||||
public static $current_lang = %s;
|
||||
}';
|
||||
|
||||
public static function addToLang(string $key, string $value = '', bool $force = false)
|
||||
{
|
||||
if (!isset(\danog\MadelineProto\Lang::$lang['en'][$key]) || $force) {
|
||||
|
@ -93,7 +93,6 @@ trait Constructors
|
||||
$this->TL->getDescriptions()['constructors'][$data['predicate']]['description'] = \danog\MadelineProto\Lang::$lang['en']['object_' . $data['predicate']];
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($this->TL->getDescriptions()['constructors'][$data['predicate']]) && !empty($data['params'])) {
|
||||
$table = '### Attributes:
|
||||
|
||||
@ -156,8 +155,7 @@ trait Constructors
|
||||
if (\in_array($ptype, ['InputEncryptedFile']) && !isset($this->settings['td'])) {
|
||||
$human_ptype = 'File path or ' . $ptype;
|
||||
}
|
||||
$table .= '|'.\str_replace('_', '\\_', $param['name']).'|'.(isset($param['subtype']) ? 'Array of ' : '').'['.\str_replace('_', '\\_', $human_ptype).'](../'.$type_or_bare_type.'/'.$ptype.'.md) | '.(isset($param['pow']) || $this->TL->getConstructors($this->td)->findByPredicate(\lcfirst($param['type']).'Empty') || ($data['type'] === 'InputMedia' && $param['name'] === 'mime_type') || ($data['type'] === 'DocumentAttribute' && \in_array($param['name'], ['w', 'h', 'duration'])) ? 'Optional' : 'Yes').'|';
|
||||
|
||||
$table .= '|' . \str_replace('_', '\\_', $param['name']) . '|' . (isset($param['subtype']) ? 'Array of ' : '') . '[' . \str_replace('_', '\\_', $human_ptype) . '](../' . $type_or_bare_type . '/' . $ptype . '.md) | ' . (isset($param['pow']) || $this->TL->getConstructors($this->td)->findByPredicate(\lcfirst($param['type']) . 'Empty') || $data['type'] === 'InputMedia' && $param['name'] === 'mime_type' || $data['type'] === 'DocumentAttribute' && \in_array($param['name'], ['w', 'h', 'duration']) ? 'Optional' : 'Yes') . '|';
|
||||
if (!isset($this->TL->getDescriptions()['constructors'][$data['predicate']]['params'][$param['name']])) {
|
||||
$this->addToLang('object_' . $data['predicate'] . '_param_' . $param['name'] . '_type_' . $param['type']);
|
||||
if (isset($this->TL->getDescriptions()['constructors'][$data['predicate']]['description'])) {
|
||||
@ -185,7 +183,7 @@ trait Constructors
|
||||
$pwr_params = '{"_": "' . $data['predicate'] . '"' . $pwr_params . '}';
|
||||
$description = isset($this->TL->getDescriptions()['constructors'][$data['predicate']]) ? $this->TL->getDescriptions()['constructors'][$data['predicate']]['description'] : $constructor . ' attributes, type and example';
|
||||
$symFile = \str_replace('.', '_', $constructor . $layer);
|
||||
$redir = $symFile !== $constructor.$layer ? "\nredirect_from: /API_docs/constructors/$symFile.html" : '';
|
||||
$redir = $symFile !== $constructor . $layer ? "\nredirect_from: /API_docs/constructors/{$symFile}.html" : '';
|
||||
$header = '---
|
||||
title: ' . $data['predicate'] . '
|
||||
description: ' . $description . '
|
||||
|
@ -85,7 +85,6 @@ trait Methods
|
||||
}
|
||||
if (!isset($this->td_descriptions['methods'][$data['method']])) {
|
||||
$this->addToLang('method_' . $data['method']);
|
||||
|
||||
if (\danog\MadelineProto\Lang::$lang['en']['method_' . $data['method']] !== '') {
|
||||
$this->td_descriptions['methods'][$data['method']]['description'] = \danog\MadelineProto\Lang::$lang['en']['method_' . $data['method']];
|
||||
}
|
||||
@ -94,7 +93,6 @@ trait Methods
|
||||
$this->docs_methods[$method] = '$MadelineProto->' . $md_method . '(\\[' . $params . '\\]) === [$' . \str_replace('_', '\\_', $type) . '](../types/' . $php_type . '.md)<a name="' . $method . '"></a>
|
||||
|
||||
';
|
||||
|
||||
if (isset($this->td_descriptions['methods'][$data['method']])) {
|
||||
$desc = \Parsedown::instance()->line(\trim(\explode("\n", $this->td_descriptions['methods'][$data['method']]['description'])[0], '.'));
|
||||
$dom = new \DOMDocument();
|
||||
@ -104,7 +102,6 @@ trait Methods
|
||||
|
||||
';
|
||||
}
|
||||
|
||||
$params = '';
|
||||
$lua_params = '';
|
||||
$pwr_params = '';
|
||||
@ -172,17 +169,15 @@ trait Methods
|
||||
$this->td_descriptions['methods'][$data['method']]['params'][$param['name']] = \danog\MadelineProto\Lang::$lang['en']['method_' . $data['method'] . '_param_' . $param['name'] . '_type_' . $param['type']];
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($this->td_descriptions['methods'][$data['method']])) {
|
||||
$table .= '|'.\str_replace('_', '\\_', $param['name']).'|'.(isset($param['subtype']) ? 'Array of ' : '').'['.\str_replace('_', '\\_', $human_ptype).'](../'.$type_or_bare_type.'/'.$ptype.'.md) | '.$this->td_descriptions['methods'][$data['method']]['params'][$param['name']].' | '.(isset($param['pow']) || (($id = $this->TL->getConstructors($this->td)->findByPredicate(\lcfirst($param['type']).'Empty')) && $id['type'] === $param['type']) || (($id = $this->TL->getConstructors($this->td)->findByPredicate('input'.$param['type'].'Empty')) && $id['type'] === $param['type']) ? 'Optional' : 'Yes').'|';
|
||||
$table .= '|' . \str_replace('_', '\\_', $param['name']) . '|' . (isset($param['subtype']) ? 'Array of ' : '') . '[' . \str_replace('_', '\\_', $human_ptype) . '](../' . $type_or_bare_type . '/' . $ptype . '.md) | ' . $this->td_descriptions['methods'][$data['method']]['params'][$param['name']] . ' | ' . (isset($param['pow']) || ($id = $this->TL->getConstructors($this->td)->findByPredicate(\lcfirst($param['type']) . 'Empty')) && $id['type'] === $param['type'] || ($id = $this->TL->getConstructors($this->td)->findByPredicate('input' . $param['type'] . 'Empty')) && $id['type'] === $param['type'] ? 'Optional' : 'Yes') . '|';
|
||||
} else {
|
||||
$table .= '|'.\str_replace('_', '\\_', $param['name']).'|'.(isset($param['subtype']) ? 'Array of ' : '').'['.\str_replace('_', '\\_', $human_ptype).'](../'.$type_or_bare_type.'/'.$ptype.'.md) | '.(isset($param['pow']) || (($id = $this->TL->getConstructors($this->td)->findByPredicate(\lcfirst($param['type']).'Empty')) && $id['type'] === $param['type']) || (($id = $this->TL->getConstructors($this->td)->findByPredicate('input'.$param['type'].'Empty')) && $id['type'] === $param['type']) ? 'Optional' : 'Yes').'|';
|
||||
$table .= '|' . \str_replace('_', '\\_', $param['name']) . '|' . (isset($param['subtype']) ? 'Array of ' : '') . '[' . \str_replace('_', '\\_', $human_ptype) . '](../' . $type_or_bare_type . '/' . $ptype . '.md) | ' . (isset($param['pow']) || ($id = $this->TL->getConstructors($this->td)->findByPredicate(\lcfirst($param['type']) . 'Empty')) && $id['type'] === $param['type'] || ($id = $this->TL->getConstructors($this->td)->findByPredicate('input' . $param['type'] . 'Empty')) && $id['type'] === $param['type'] ? 'Optional' : 'Yes') . '|';
|
||||
}
|
||||
$table .= PHP_EOL;
|
||||
$pptype = \in_array($ptype, ['string', 'bytes']) ? "'" . $ptype . "'" : $ptype;
|
||||
$ppptype = \in_array($ptype, ['string']) ? '"' . $ptype . '"' : $ptype;
|
||||
$ppptype = \in_array($ptype, ['bytes']) ? '{"_": "bytes", "bytes":"base64 encoded ' . $ptype . '"}' : $ppptype;
|
||||
|
||||
$params .= "'" . $param['name'] . "' => ";
|
||||
$params .= (isset($param['subtype']) ? '[' . $pptype . ', ' . $pptype . ']' : $pptype) . ', ';
|
||||
$json_params .= '"' . $param['name'] . '": ' . (isset($param['subtype']) ? '[' . $ppptype . ']' : $ppptype) . ', ';
|
||||
@ -207,7 +202,7 @@ trait Methods
|
||||
}
|
||||
$description = isset($this->td_descriptions['methods'][$data['method']]) ? $this->td_descriptions['methods'][$data['method']]['description'] : $data['method'] . ' parameters, return type and example';
|
||||
$symFile = \str_replace('.', '_', $method);
|
||||
$redir = $symFile !== $method ? "\nredirect_from: /API_docs/methods/$symFile.html" : '';
|
||||
$redir = $symFile !== $method ? "\nredirect_from: /API_docs/methods/{$symFile}.html" : '';
|
||||
$header = '---
|
||||
title: ' . $data['method'] . '
|
||||
description: ' . $description . '
|
||||
@ -250,7 +245,7 @@ if (!file_exists(\'madeline.php\')) {
|
||||
}
|
||||
include \'madeline.php\';
|
||||
|
||||
$MadelineProto = new \danog\MadelineProto\API(\'session.madeline\');
|
||||
$MadelineProto = new \\danog\\MadelineProto\\API(\'session.madeline\');
|
||||
$MadelineProto->start();
|
||||
|
||||
$' . $type . ' = $MadelineProto->' . $php_method . '([' . $params . ']);
|
||||
@ -328,7 +323,7 @@ MadelineProto supports all html entities supported by [html_entity_decode](http:
|
||||
';
|
||||
foreach ($new['result'][$data['method']] as $error) {
|
||||
[$error, $code] = $error;
|
||||
$example .= "|$code|$error|".$errors['human_result'][$error][0].'|'."\n";
|
||||
$example .= "|{$code}|{$error}|" . $errors['human_result'][$error][0] . '|' . "\n";
|
||||
}
|
||||
$example .= "\n\n";
|
||||
}
|
||||
@ -384,7 +379,6 @@ $MadelineProto->[requestCall](https://docs.madelineproto.xyz/requestCall.html)($
|
||||
$MadelineProto->[requestSecretChat](https://docs.madelineproto.xyz/requestSecretChat.html)($id);
|
||||
|
||||
' . \implode('', $this->docs_methods));
|
||||
|
||||
\file_put_contents('methods/' . $this->index, '---
|
||||
title: Methods
|
||||
description: What do you want to do?
|
||||
|
@ -23,12 +23,10 @@ class Exception extends \Exception
|
||||
{
|
||||
use TL\PrettyException;
|
||||
public static $rollbar = true;
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
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:' . PHP_EOL . $this->getTLTrace();
|
||||
}
|
||||
|
||||
public function __construct($message = null, $code = 0, self $previous = null, $file = null, $line = null)
|
||||
{
|
||||
$this->prettifyTL();
|
||||
@ -42,7 +40,6 @@ class Exception extends \Exception
|
||||
if (\strpos($message, 'socket_accept') === false) {
|
||||
\danog\MadelineProto\Logger::log($message . ' in ' . \basename($this->file) . ':' . $this->line, \danog\MadelineProto\Logger::FATAL_ERROR);
|
||||
}
|
||||
|
||||
if (\in_array($message, ['The session is corrupted!', 'Re-executing query...', 'I had to recreate the temporary authorization key', 'This peer is not present in the internal peer database', "Couldn't get response", 'Chat forbidden', 'The php-libtgvoip extension is required to accept and manage calls. See daniil.it/MadelineProto for more info.', 'File does not exist', 'Please install this fork of phpseclib: https://github.com/danog/phpseclib'])) {
|
||||
return;
|
||||
}
|
||||
@ -53,7 +50,6 @@ class Exception extends \Exception
|
||||
\Rollbar\Rollbar::log(\Rollbar\Payload\Level::error(), $this, \debug_backtrace(0));
|
||||
}
|
||||
}
|
||||
|
||||
public static function extension(string $extensionName)
|
||||
{
|
||||
$additional = 'Try running sudo apt-get install php' . PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION . '-' . $extensionName . '.';
|
||||
@ -78,17 +74,11 @@ class Exception extends \Exception
|
||||
public static function exceptionErrorHandler($errno = 0, $errstr = null, $errfile = null, $errline = null)
|
||||
{
|
||||
// If error is suppressed with @, don't throw an exception
|
||||
if (\error_reporting() === 0
|
||||
|| \strpos($errstr, 'headers already sent')
|
||||
|| ($errfile
|
||||
&& (\strpos($errfile, 'vendor/amphp') !== false || \strpos($errfile, 'vendor/league') !== false))
|
||||
) {
|
||||
if (\error_reporting() === 0 || \strpos($errstr, 'headers already sent') || $errfile && (\strpos($errfile, 'vendor/amphp') !== false || \strpos($errfile, 'vendor/league') !== false)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
throw new self($errstr, $errno, null, $errfile, $errline);
|
||||
}
|
||||
|
||||
/**
|
||||
* ExceptionErrorHandler.
|
||||
*
|
||||
|
@ -36,7 +36,6 @@ class FileCallback implements FileCallbackInterface
|
||||
* @var callable
|
||||
*/
|
||||
private $callback;
|
||||
|
||||
/**
|
||||
* Construct file callback.
|
||||
*
|
||||
@ -48,7 +47,6 @@ class FileCallback implements FileCallbackInterface
|
||||
$this->file = $file;
|
||||
$this->callback = $callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get file.
|
||||
*
|
||||
@ -58,7 +56,6 @@ class FileCallback implements FileCallbackInterface
|
||||
{
|
||||
return $this->file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke callback.
|
||||
*
|
||||
@ -71,7 +68,6 @@ class FileCallback implements FileCallbackInterface
|
||||
public function __invoke($percent, $speed, $time)
|
||||
{
|
||||
$callback = $this->callback;
|
||||
|
||||
return $callback($percent, $speed, $time);
|
||||
}
|
||||
}
|
||||
|
@ -30,7 +30,6 @@ interface FileCallbackInterface
|
||||
* @return mixed
|
||||
*/
|
||||
public function getFile();
|
||||
|
||||
/**
|
||||
* Invoke callback.
|
||||
*
|
||||
|
@ -30,12 +30,10 @@ use function Amp\ByteStream\getStdout;
|
||||
class Logger
|
||||
{
|
||||
use Tools;
|
||||
|
||||
const FOREGROUND = ['default' => 39, 'black' => 30, 'red' => 31, 'green' => 32, 'yellow' => 33, 'blue' => 34, 'magenta' => 35, 'cyan' => 36, 'light_gray' => 37, 'dark_gray' => 90, 'light_red' => 91, 'light_green' => 92, 'light_yellow' => 93, 'light_blue' => 94, 'light_magenta' => 95, 'light_cyan' => 96, 'white' => 97];
|
||||
const BACKGROUND = ['default' => 49, 'black' => 40, 'red' => 41, 'magenta' => 45, 'yellow' => 43, 'green' => 42, 'blue' => 44, 'cyan' => 46, 'light_gray' => 47, 'dark_gray' => 100, 'light_red' => 101, 'light_green' => 102, 'light_yellow' => 103, 'light_blue' => 104, 'light_magenta' => 105, 'light_cyan' => 106, 'white' => 107];
|
||||
const SET = ['bold' => 1, 'dim' => 2, 'underlined' => 3, 'blink' => 4, 'reverse' => 5, 'hidden' => 6];
|
||||
const RESET = ['all' => 0, 'bold' => 21, 'dim' => 22, 'underlined' => 24, 'blink' => 25, 'reverse' => 26, 'hidden' => 28];
|
||||
|
||||
/**
|
||||
* Logging mode.
|
||||
*
|
||||
@ -72,7 +70,6 @@ class Logger
|
||||
* @var string
|
||||
*/
|
||||
public $newline = "\n";
|
||||
|
||||
/**
|
||||
* Default logger instance.
|
||||
*
|
||||
@ -85,20 +82,17 @@ class Logger
|
||||
* @var boolean
|
||||
*/
|
||||
public static $printed = false;
|
||||
|
||||
const ULTRA_VERBOSE = 5;
|
||||
const VERBOSE = 4;
|
||||
const NOTICE = 3;
|
||||
const WARNING = 2;
|
||||
const ERROR = 1;
|
||||
const FATAL_ERROR = 0;
|
||||
|
||||
const NO_LOGGER = 0;
|
||||
const DEFAULT_LOGGER = 1;
|
||||
const FILE_LOGGER = 2;
|
||||
const ECHO_LOGGER = 3;
|
||||
const CALLABLE_LOGGER = 4;
|
||||
|
||||
/**
|
||||
* Construct global static logger from MadelineProto settings.
|
||||
*
|
||||
@ -135,12 +129,10 @@ class Logger
|
||||
if (PHP_SAPI !== 'cli' && isset($settings['logger']['logger_param']) && $settings['logger']['logger_param'] === 'MadelineProto.log') {
|
||||
$settings['logger']['logger_param'] = Magic::$script_cwd . '/MadelineProto.log';
|
||||
}
|
||||
|
||||
$logger = new self($settings['logger']['logger'], $settings['logger']['logger_param'] ?? '', $prefix, $settings['logger']['logger_level'] ?? Logger::VERBOSE, $settings['logger']['max_size'] ?? 100 * 1024 * 1024);
|
||||
if (!self::$default) {
|
||||
self::$default = $logger;
|
||||
}
|
||||
|
||||
if (PHP_SAPI !== 'cli') {
|
||||
try {
|
||||
\error_reporting(E_ALL);
|
||||
@ -151,10 +143,8 @@ class Logger
|
||||
$logger->logger('Could not enable PHP logging');
|
||||
}
|
||||
}
|
||||
|
||||
return $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct global logger.
|
||||
*
|
||||
@ -170,7 +160,6 @@ class Logger
|
||||
{
|
||||
self::$default = new self($mode, $optional, $prefix, $level, $max_size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct global logger.
|
||||
*
|
||||
@ -191,19 +180,15 @@ class Logger
|
||||
$this->optional = $mode == 2 ? Absolute::absolute($optional) : $optional;
|
||||
$this->prefix = $prefix === '' ? '' : ', ' . $prefix;
|
||||
$this->level = $level;
|
||||
|
||||
if ($this->mode === 2 && !\file_exists(\pathinfo($this->optional, PATHINFO_DIRNAME))) {
|
||||
$this->optional = Magic::$script_cwd . '/MadelineProto.log';
|
||||
}
|
||||
|
||||
if ($this->mode === 2 && !\preg_match('/\.log$/', $this->optional)) {
|
||||
if ($this->mode === 2 && !\preg_match('/\\.log$/', $this->optional)) {
|
||||
$this->optional .= '.log';
|
||||
}
|
||||
|
||||
if ($mode === 2 && $max_size !== -1 && \file_exists($this->optional) && \filesize($this->optional) > $max_size) {
|
||||
\unlink($this->optional);
|
||||
}
|
||||
|
||||
$this->colors[self::ULTRA_VERBOSE] = \implode(';', [self::FOREGROUND['light_gray'], self::SET['dim']]);
|
||||
$this->colors[self::VERBOSE] = \implode(';', [self::FOREGROUND['green'], self::SET['bold']]);
|
||||
$this->colors[self::NOTICE] = \implode(';', [self::FOREGROUND['yellow'], self::SET['bold']]);
|
||||
@ -211,7 +196,6 @@ class Logger
|
||||
$this->colors[self::ERROR] = \implode(';', [self::FOREGROUND['white'], self::SET['bold'], self::BACKGROUND['red']]);
|
||||
$this->colors[self::FATAL_ERROR] = \implode(';', [self::FOREGROUND['red'], self::SET['bold'], self::BACKGROUND['light_gray']]);
|
||||
$this->newline = PHP_EOL;
|
||||
|
||||
if ($this->mode === 3) {
|
||||
$this->stdout = getStdout();
|
||||
if (PHP_SAPI !== 'cli') {
|
||||
@ -230,7 +214,6 @@ class Logger
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Log a message.
|
||||
*
|
||||
@ -247,7 +230,6 @@ class Logger
|
||||
echo $param . PHP_EOL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Log a message.
|
||||
*
|
||||
@ -265,12 +247,10 @@ class Logger
|
||||
if (!self::$printed) {
|
||||
self::$printed = true;
|
||||
$this->colors[self::NOTICE] = \implode(';', [self::FOREGROUND['light_gray'], self::SET['bold'], self::BACKGROUND['blue']]);
|
||||
|
||||
$this->logger('MadelineProto');
|
||||
$this->logger('Copyright (C) 2016-2019 Daniil Gentili');
|
||||
$this->logger('Licensed under AGPLv3');
|
||||
$this->logger('https://github.com/danog/MadelineProto');
|
||||
|
||||
$this->colors[self::NOTICE] = \implode(';', [self::FOREGROUND['yellow'], self::SET['bold']]);
|
||||
}
|
||||
if ($this->mode === 4) {
|
||||
@ -300,8 +280,12 @@ class Logger
|
||||
$param = Magic::$isatty ? "\33[" . $this->colors[$level] . 'm' . $param . "\33[0m" . $this->newline : $param . $this->newline;
|
||||
if ($this->stdout->write($param) instanceof Failure) {
|
||||
switch ($this->mode) {
|
||||
case 3: echo $param; break;
|
||||
case 2: \file_put_contents($this->optional, $param, FILE_APPEND); break;
|
||||
case 3:
|
||||
echo $param;
|
||||
break;
|
||||
case 2:
|
||||
\file_put_contents($this->optional, $param, FILE_APPEND);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* RPC call status check loop.
|
||||
*
|
||||
@ -43,7 +44,6 @@ class CheckLoop extends ResumableSignalLoop
|
||||
* @var string
|
||||
*/
|
||||
protected $datacenter;
|
||||
|
||||
/**
|
||||
* DataCenterConnection instance.
|
||||
*
|
||||
@ -57,48 +57,44 @@ class CheckLoop extends ResumableSignalLoop
|
||||
$this->datacenter = $connection->getDatacenterID();
|
||||
$this->datacenterConnection = $connection->getShared();
|
||||
}
|
||||
|
||||
public function loop()
|
||||
public function loop(): \Generator
|
||||
{
|
||||
$API = $this->API;
|
||||
$datacenter = $this->datacenter;
|
||||
$connection = $this->connection;
|
||||
$shared = $this->datacenterConnection;
|
||||
|
||||
$timeout = $shared->getSettings()['timeout'];
|
||||
$timeoutResend = $timeout * $timeout; // Typically 25 seconds, good enough
|
||||
$timeoutResend = $timeout * $timeout;
|
||||
// Typically 25 seconds, good enough
|
||||
while (true) {
|
||||
while (empty($connection->new_outgoing)) {
|
||||
if (yield $this->waitSignal($this->pause())) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if ($connection->hasPendingCalls()) {
|
||||
$last_msgid = $connection->getMaxId(true);
|
||||
$last_chunk = $connection->getLastChunk();
|
||||
|
||||
if ($shared->hasTempAuthKey()) {
|
||||
$full_message_ids = $connection->getPendingCalls(); //array_values($connection->new_outgoing);
|
||||
$full_message_ids = $connection->getPendingCalls();
|
||||
//array_values($connection->new_outgoing);
|
||||
foreach (\array_chunk($full_message_ids, 8192) as $message_ids) {
|
||||
$deferred = new Deferred();
|
||||
$deferred->promise()->onResolve(
|
||||
function ($e, $result) use ($message_ids, $API, $connection, $datacenter, $timeoutResend) {
|
||||
$deferred->promise()->onResolve(function ($e, $result) use ($message_ids, $API, $connection, $datacenter, $timeoutResend) {
|
||||
if ($e) {
|
||||
$API->logger("Got exception in check loop for DC $datacenter");
|
||||
$API->logger("Got exception in check loop for DC {$datacenter}");
|
||||
$API->logger((string) $e);
|
||||
|
||||
return;
|
||||
}
|
||||
$reply = [];
|
||||
foreach (\str_split($result['info']) as $key => $chr) {
|
||||
$message_id = $message_ids[$key];
|
||||
if (!isset($connection->outgoing_messages[$message_id])) {
|
||||
$API->logger->logger('Already got response for and forgot about message ID '.($message_id));
|
||||
$API->logger->logger('Already got response for and forgot about message ID ' . $message_id);
|
||||
continue;
|
||||
}
|
||||
if (!isset($connection->new_outgoing[$message_id])) {
|
||||
$API->logger->logger('Already got response for '.$connection->outgoing_messages[$message_id]['_'].' with message ID '.($message_id));
|
||||
$API->logger->logger('Already got response for ' . $connection->outgoing_messages[$message_id]['_'] . ' with message ID ' . $message_id);
|
||||
continue;
|
||||
}
|
||||
$chr = \ord($chr);
|
||||
@ -113,25 +109,25 @@ class CheckLoop extends ResumableSignalLoop
|
||||
$connection->gotResponseForOutgoingMessageId($message_id);
|
||||
break;
|
||||
}
|
||||
$API->logger->logger('Message '.$connection->outgoing_messages[$message_id]['_'].' with message ID '.($message_id).' not received by server, resending...', \danog\MadelineProto\Logger::ERROR);
|
||||
$API->logger->logger('Message ' . $connection->outgoing_messages[$message_id]['_'] . ' with message ID ' . $message_id . ' not received by server, resending...', \danog\MadelineProto\Logger::ERROR);
|
||||
$connection->methodRecall('watcherId', ['message_id' => $message_id, 'postpone' => true]);
|
||||
break;
|
||||
case 4:
|
||||
if ($chr & 32) {
|
||||
if ($connection->outgoing_messages[$message_id]['sent'] + $timeoutResend < \time()) {
|
||||
$API->logger->logger('Message '.$connection->outgoing_messages[$message_id]['_'].' with message ID '.($message_id).' received by server and is being processed for way too long, resending request...', \danog\MadelineProto\Logger::ERROR);
|
||||
$API->logger->logger('Message ' . $connection->outgoing_messages[$message_id]['_'] . ' with message ID ' . $message_id . ' received by server and is being processed for way too long, resending request...', \danog\MadelineProto\Logger::ERROR);
|
||||
$connection->methodRecall('', ['message_id' => $message_id, 'postpone' => true]);
|
||||
} else {
|
||||
$API->logger->logger('Message '.$connection->outgoing_messages[$message_id]['_'].' with message ID '.($message_id).' received by server and is being processed, waiting...', \danog\MadelineProto\Logger::ERROR);
|
||||
$API->logger->logger('Message ' . $connection->outgoing_messages[$message_id]['_'] . ' with message ID ' . $message_id . ' received by server and is being processed, waiting...', \danog\MadelineProto\Logger::ERROR);
|
||||
}
|
||||
} elseif ($chr & 64) {
|
||||
$API->logger->logger('Message '.$connection->outgoing_messages[$message_id]['_'].' with message ID '.($message_id).' received by server and was already processed, requesting reply...', \danog\MadelineProto\Logger::ERROR);
|
||||
$API->logger->logger('Message ' . $connection->outgoing_messages[$message_id]['_'] . ' with message ID ' . $message_id . ' received by server and was already processed, requesting reply...', \danog\MadelineProto\Logger::ERROR);
|
||||
$reply[] = $message_id;
|
||||
} elseif ($chr & 128) {
|
||||
$API->logger->logger('Message '.$connection->outgoing_messages[$message_id]['_'].' with message ID '.($message_id).' received by server and was already sent, requesting reply...', \danog\MadelineProto\Logger::ERROR);
|
||||
$API->logger->logger('Message ' . $connection->outgoing_messages[$message_id]['_'] . ' with message ID ' . $message_id . ' received by server and was already sent, requesting reply...', \danog\MadelineProto\Logger::ERROR);
|
||||
$reply[] = $message_id;
|
||||
} else {
|
||||
$API->logger->logger('Message '.$connection->outgoing_messages[$message_id]['_'].' with message ID '.($message_id).' received by server, requesting reply...', \danog\MadelineProto\Logger::ERROR);
|
||||
$API->logger->logger('Message ' . $connection->outgoing_messages[$message_id]['_'] . ' with message ID ' . $message_id . ' received by server, requesting reply...', \danog\MadelineProto\Logger::ERROR);
|
||||
$reply[] = $message_id;
|
||||
}
|
||||
}
|
||||
@ -140,23 +136,19 @@ class CheckLoop extends ResumableSignalLoop
|
||||
\danog\MadelineProto\Tools::callFork($connection->objectCall('msg_resend_ans_req', ['msg_ids' => $reply], ['postpone' => true]));
|
||||
}
|
||||
$connection->flush();
|
||||
}
|
||||
);
|
||||
});
|
||||
$list = '';
|
||||
// Don't edit this here pls
|
||||
foreach ($message_ids as $message_id) {
|
||||
$list .= $connection->outgoing_messages[$message_id]['_'] . ', ';
|
||||
}
|
||||
$API->logger->logger("Still missing $list on DC $datacenter, sending state request", \danog\MadelineProto\Logger::ERROR);
|
||||
$API->logger->logger("Still missing {$list} on DC {$datacenter}, sending state request", \danog\MadelineProto\Logger::ERROR);
|
||||
yield $connection->objectCall('msgs_state_req', ['msg_ids' => $message_ids], ['promise' => $deferred]);
|
||||
}
|
||||
} else {
|
||||
foreach ($connection->new_outgoing as $message_id) {
|
||||
if (isset($connection->outgoing_messages[$message_id]['sent'])
|
||||
&& $connection->outgoing_messages[$message_id]['sent'] + $timeout < \time()
|
||||
&& $connection->outgoing_messages[$message_id]['unencrypted']
|
||||
) {
|
||||
$API->logger->logger('Still missing '.$connection->outgoing_messages[$message_id]['_'].' with message id '.($message_id)." on DC $datacenter, resending", \danog\MadelineProto\Logger::ERROR);
|
||||
if (isset($connection->outgoing_messages[$message_id]['sent']) && $connection->outgoing_messages[$message_id]['sent'] + $timeout < \time() && $connection->outgoing_messages[$message_id]['unencrypted']) {
|
||||
$API->logger->logger('Still missing ' . $connection->outgoing_messages[$message_id]['_'] . ' with message id ' . $message_id . " on DC {$datacenter}, resending", \danog\MadelineProto\Logger::ERROR);
|
||||
$connection->methodRecall('', ['message_id' => $message_id, 'postpone' => true]);
|
||||
}
|
||||
}
|
||||
@ -165,12 +157,10 @@ class CheckLoop extends ResumableSignalLoop
|
||||
if (yield $this->waitSignal($this->pause($timeout))) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($connection->getMaxId(true) === $last_msgid && $connection->getLastChunk() === $last_chunk) {
|
||||
$API->logger->logger("We did not receive a response for $timeout seconds: reconnecting and exiting check loop on DC $datacenter");
|
||||
$API->logger->logger("We did not receive a response for {$timeout} seconds: reconnecting and exiting check loop on DC {$datacenter}");
|
||||
//$this->exitedLoop();
|
||||
Tools::callForkDefer($connection->reconnect());
|
||||
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
@ -180,7 +170,6 @@ class CheckLoop extends ResumableSignalLoop
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
return "check loop in DC {$this->datacenter}";
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* HttpWait loop.
|
||||
*
|
||||
@ -40,14 +41,12 @@ class HttpWaitLoop extends ResumableSignalLoop
|
||||
* @var string
|
||||
*/
|
||||
protected $datacenter;
|
||||
|
||||
/**
|
||||
* DataCenterConnection instance.
|
||||
*
|
||||
* @var \danog\MadelineProto\DataCenterConnection
|
||||
*/
|
||||
protected $datacenterConnection;
|
||||
|
||||
public function __construct(Connection $connection)
|
||||
{
|
||||
$this->connection = $connection;
|
||||
@ -55,18 +54,15 @@ class HttpWaitLoop extends ResumableSignalLoop
|
||||
$this->datacenter = $connection->getDatacenterID();
|
||||
$this->datacenterConnection = $connection->getShared();
|
||||
}
|
||||
|
||||
public function loop()
|
||||
public function loop(): \Generator
|
||||
{
|
||||
$API = $this->API;
|
||||
$datacenter = $this->datacenter;
|
||||
$connection = $this->connection;
|
||||
$shared = $this->datacenterConnection;
|
||||
|
||||
if (!$shared->isHttp()) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
if (yield $this->waitSignal($this->pause())) {
|
||||
return;
|
||||
@ -79,14 +75,13 @@ class HttpWaitLoop extends ResumableSignalLoop
|
||||
return;
|
||||
}
|
||||
}
|
||||
$API->logger->logger("DC $datacenter: request {$connection->countHttpSent()}, response {$connection->countHttpReceived()}");
|
||||
if ($connection->countHttpSent() === $connection->countHttpReceived() && (!empty($connection->pending_outgoing) || (!empty($connection->new_outgoing) && !$connection->hasPendingCalls()))) {
|
||||
$API->logger->logger("DC {$datacenter}: request {$connection->countHttpSent()}, response {$connection->countHttpReceived()}");
|
||||
if ($connection->countHttpSent() === $connection->countHttpReceived() && (!empty($connection->pending_outgoing) || !empty($connection->new_outgoing) && !$connection->hasPendingCalls())) {
|
||||
yield $connection->sendMessage(['_' => 'http_wait', 'body' => ['max_wait' => 30000, 'wait_after' => 0, 'max_delay' => 0], 'contentRelated' => true, 'unencrypted' => false, 'method' => false]);
|
||||
}
|
||||
$API->logger->logger("DC $datacenter: request {$connection->countHttpSent()}, response {$connection->countHttpReceived()}");
|
||||
$API->logger->logger("DC {$datacenter}: request {$connection->countHttpSent()}, response {$connection->countHttpReceived()}");
|
||||
}
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
return "HTTP wait loop in DC {$this->datacenter}";
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Ping loop.
|
||||
*
|
||||
@ -40,14 +41,12 @@ class PingLoop extends ResumableSignalLoop
|
||||
* @var string
|
||||
*/
|
||||
protected $datacenter;
|
||||
|
||||
/**
|
||||
* DataCenterConnection instance.
|
||||
*
|
||||
* @var \danog\MadelineProto\DataCenterConnection
|
||||
*/
|
||||
protected $datacenterConnection;
|
||||
|
||||
public function __construct(Connection $connection)
|
||||
{
|
||||
$this->connection = $connection;
|
||||
@ -55,14 +54,12 @@ class PingLoop extends ResumableSignalLoop
|
||||
$this->datacenter = $connection->getDatacenterID();
|
||||
$this->datacenterConnection = $connection->getShared();
|
||||
}
|
||||
|
||||
public function loop()
|
||||
public function loop(): \Generator
|
||||
{
|
||||
$API = $this->API;
|
||||
$datacenter = $this->datacenter;
|
||||
$connection = $this->connection;
|
||||
$shared = $this->datacenterConnection;
|
||||
|
||||
$timeout = $shared->getSettings()['timeout'];
|
||||
while (true) {
|
||||
while (!$shared->hasTempAuthKey()) {
|
||||
@ -74,17 +71,16 @@ class PingLoop extends ResumableSignalLoop
|
||||
return;
|
||||
}
|
||||
if (\time() - $connection->getLastChunk() >= $timeout) {
|
||||
$API->logger->logger("Ping DC $datacenter");
|
||||
$API->logger->logger("Ping DC {$datacenter}");
|
||||
try {
|
||||
yield $connection->methodCallAsyncRead('ping', ['ping_id' => \random_bytes(8)]);
|
||||
} catch (\Throwable $e) {
|
||||
$API->logger->logger("Error while pinging DC $datacenter");
|
||||
$API->logger->logger("Error while pinging DC {$datacenter}");
|
||||
$API->logger->logger((string) $e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
return "Ping loop in DC {$this->datacenter}";
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Socket read loop.
|
||||
*
|
||||
@ -38,7 +39,6 @@ class ReadLoop extends SignalLoop
|
||||
{
|
||||
use Tools;
|
||||
use Crypt;
|
||||
|
||||
/**
|
||||
* Connection instance.
|
||||
*
|
||||
@ -57,7 +57,6 @@ class ReadLoop extends SignalLoop
|
||||
* @var string
|
||||
*/
|
||||
protected $datacenter;
|
||||
|
||||
public function __construct(Connection $connection)
|
||||
{
|
||||
$this->connection = $connection;
|
||||
@ -65,14 +64,12 @@ class ReadLoop extends SignalLoop
|
||||
$this->datacenter = $connection->getDatacenterID();
|
||||
$this->datacenterConnection = $connection->getShared();
|
||||
}
|
||||
|
||||
public function loop()
|
||||
public function loop(): \Generator
|
||||
{
|
||||
$API = $this->API;
|
||||
$datacenter = $this->datacenter;
|
||||
$connection = $this->connection;
|
||||
$shared = $this->datacenterConnection;
|
||||
|
||||
while (true) {
|
||||
try {
|
||||
$error = yield $this->waitSignal($this->readMessage());
|
||||
@ -80,18 +77,16 @@ class ReadLoop extends SignalLoop
|
||||
if ($connection->shouldReconnect()) {
|
||||
return;
|
||||
}
|
||||
Tools::callForkDefer((function () use ($API, $connection, $datacenter, $e) {
|
||||
Tools::callForkDefer((function () use ($API, $connection, $datacenter, $e): \Generator {
|
||||
$API->logger->logger($e);
|
||||
$API->logger->logger("Got nothing in the socket in DC {$datacenter}, reconnecting...", Logger::ERROR);
|
||||
yield $connection->reconnect();
|
||||
})());
|
||||
return;
|
||||
}
|
||||
|
||||
if (\is_int($error)) {
|
||||
//$this->exitedLoop();
|
||||
|
||||
Tools::callForkDefer((function () use ($error, $shared, $connection, $datacenter, $API) {
|
||||
Tools::callForkDefer((function () use ($error, $shared, $connection, $datacenter, $API): \Generator {
|
||||
if ($error === -404) {
|
||||
if ($shared->hasTempAuthKey()) {
|
||||
$API->logger->logger("WARNING: Resetting auth key in DC {$datacenter}...", \danog\MadelineProto\Logger::WARNING);
|
||||
@ -117,70 +112,53 @@ class ReadLoop extends SignalLoop
|
||||
yield $connection->reconnect();
|
||||
} else {
|
||||
yield $connection->reconnect();
|
||||
|
||||
throw new \danog\MadelineProto\RPCErrorException($error, $error);
|
||||
}
|
||||
})());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$connection->httpReceived();
|
||||
|
||||
Loop::defer([$connection, 'handleMessages']);
|
||||
|
||||
if ($shared->isHttp()) {
|
||||
Loop::defer([$connection, 'pingHttpWaiter']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function readMessage()
|
||||
public function readMessage(): \Generator
|
||||
{
|
||||
$API = $this->API;
|
||||
$datacenter = $this->datacenter;
|
||||
$connection = $this->connection;
|
||||
$shared = $this->datacenterConnection;
|
||||
|
||||
if ($connection->shouldReconnect()) {
|
||||
$API->logger->logger('Not reading because connection is old');
|
||||
|
||||
throw new NothingInTheSocketException();
|
||||
}
|
||||
|
||||
try {
|
||||
$buffer = yield $connection->stream->getReadBuffer($payload_length);
|
||||
} catch (ClosedException $e) {
|
||||
$API->logger->logger($e->getReason());
|
||||
if (\strpos($e->getReason(), ' ') === 0) {
|
||||
$payload = -\substr($e->getReason(), 7);
|
||||
$API->logger->logger("Received $payload from DC ".$datacenter, \danog\MadelineProto\Logger::ERROR);
|
||||
|
||||
$API->logger->logger("Received {$payload} from DC " . $datacenter, \danog\MadelineProto\Logger::ERROR);
|
||||
return $payload;
|
||||
}
|
||||
|
||||
throw $e;
|
||||
}
|
||||
|
||||
if ($payload_length === 4) {
|
||||
$payload = \danog\MadelineProto\Tools::unpackSignedInt(yield $buffer->bufferRead(4));
|
||||
$API->logger->logger("Received $payload from DC ".$datacenter, \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
|
||||
$API->logger->logger("Received {$payload} from DC " . $datacenter, \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
return $payload;
|
||||
}
|
||||
|
||||
$connection->reading(true);
|
||||
try {
|
||||
$auth_key_id = yield $buffer->bufferRead(8);
|
||||
|
||||
if ($auth_key_id === "\0\0\0\0\0\0\0\0") {
|
||||
$message_id = yield $buffer->bufferRead(8);
|
||||
if (!\in_array($message_id, [1, 0])) {
|
||||
$connection->checkMessageId($message_id, ['outgoing' => false, 'container' => false]);
|
||||
}
|
||||
|
||||
$message_length = \unpack('V', yield $buffer->bufferRead(4))[1];
|
||||
|
||||
$message_data = yield $buffer->bufferRead($message_length);
|
||||
$left = $payload_length - $message_length - 4 - 8 - 8;
|
||||
if ($left) {
|
||||
@ -195,7 +173,6 @@ class ReadLoop extends SignalLoop
|
||||
$message_key = yield $buffer->bufferRead(16);
|
||||
list($aes_key, $aes_iv) = $this->aesCalculate($message_key, $shared->getTempAuthKey()->getAuthKey(), false);
|
||||
$encrypted_data = yield $buffer->bufferRead($payload_length - 24);
|
||||
|
||||
$protocol_padding = \strlen($encrypted_data) % 16;
|
||||
if ($protocol_padding) {
|
||||
$encrypted_data = \substr($encrypted_data, 0, -$protocol_padding);
|
||||
@ -216,7 +193,6 @@ class ReadLoop extends SignalLoop
|
||||
$message_id = \substr($decrypted_data, 16, 8);
|
||||
$connection->checkMessageId($message_id, ['outgoing' => false, 'container' => false]);
|
||||
$seq_no = \unpack('V', \substr($decrypted_data, 24, 4))[1];
|
||||
|
||||
$message_data_length = \unpack('V', \substr($decrypted_data, 28, 4))[1];
|
||||
if ($message_data_length > \strlen($decrypted_data)) {
|
||||
throw new \danog\MadelineProto\SecurityException('message_data_length is too big');
|
||||
@ -240,24 +216,20 @@ class ReadLoop extends SignalLoop
|
||||
$connection->incoming_messages[$message_id] = ['seq_no' => $seq_no];
|
||||
} else {
|
||||
$API->logger->logger('Got unknown auth_key id', \danog\MadelineProto\Logger::ERROR);
|
||||
|
||||
return -404;
|
||||
}
|
||||
$deserialized = $API->getTL()->deserialize($message_data, ['type' => '', 'connection' => $connection]);
|
||||
$API->referenceDatabase->reset();
|
||||
|
||||
$connection->incoming_messages[$message_id]['content'] = $deserialized;
|
||||
$connection->incoming_messages[$message_id]['response'] = -1;
|
||||
$connection->new_incoming[$message_id] = $message_id;
|
||||
//$connection->last_http_wait = 0;
|
||||
|
||||
$API->logger->logger('Received payload from DC ' . $datacenter, \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
} finally {
|
||||
$connection->reading(false);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
return "read loop in DC {$this->datacenter}";
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Socket write loop.
|
||||
*
|
||||
@ -34,7 +35,6 @@ class WriteLoop extends ResumableSignalLoop
|
||||
{
|
||||
use Crypt;
|
||||
use Tools;
|
||||
|
||||
/**
|
||||
* Connection instance.
|
||||
*
|
||||
@ -53,7 +53,6 @@ class WriteLoop extends ResumableSignalLoop
|
||||
* @var string
|
||||
*/
|
||||
protected $datacenter;
|
||||
|
||||
public function __construct(Connection $connection)
|
||||
{
|
||||
$this->connection = $connection;
|
||||
@ -62,14 +61,12 @@ class WriteLoop extends ResumableSignalLoop
|
||||
$ctx = $connection->getCtx();
|
||||
$this->datacenter = $connection->getDatacenterID();
|
||||
}
|
||||
|
||||
public function loop(): \Generator
|
||||
{
|
||||
$API = $this->API;
|
||||
$connection = $this->connection;
|
||||
$shared = $this->datacenterConnection;
|
||||
$datacenter = $this->datacenter;
|
||||
|
||||
$please_wait = false;
|
||||
while (true) {
|
||||
while (empty($connection->pending_outgoing) || $please_wait) {
|
||||
@ -77,21 +74,18 @@ class WriteLoop extends ResumableSignalLoop
|
||||
$API->logger->logger('Not writing because connection is old');
|
||||
return;
|
||||
}
|
||||
|
||||
$please_wait = false;
|
||||
$API->logger->logger("Waiting in $this", Logger::ULTRA_VERBOSE);
|
||||
$API->logger->logger("Waiting in {$this}", Logger::ULTRA_VERBOSE);
|
||||
if (yield $this->waitSignal($this->pause())) {
|
||||
$API->logger->logger("Exiting $this", Logger::ULTRA_VERBOSE);
|
||||
$API->logger->logger("Exiting {$this}", Logger::ULTRA_VERBOSE);
|
||||
return;
|
||||
}
|
||||
$API->logger->logger("Done waiting in $this", Logger::ULTRA_VERBOSE);
|
||||
|
||||
$API->logger->logger("Done waiting in {$this}", Logger::ULTRA_VERBOSE);
|
||||
if ($connection->shouldReconnect()) {
|
||||
$API->logger->logger('Not writing because connection is old');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$connection->writing(true);
|
||||
try {
|
||||
$please_wait = yield $this->{$shared->hasTempAuthKey() ? 'encryptedWriteLoop' : 'unencryptedWriteLoop'}();
|
||||
@ -99,7 +93,7 @@ class WriteLoop extends ResumableSignalLoop
|
||||
if ($connection->shouldReconnect()) {
|
||||
return;
|
||||
}
|
||||
Tools::callForkDefer((function () use ($API, $connection, $datacenter, $e) {
|
||||
Tools::callForkDefer((function () use ($API, $connection, $datacenter, $e): \Generator {
|
||||
$API->logger->logger($e);
|
||||
$API->logger->logger("Got nothing in the socket in DC {$datacenter}, reconnecting...", Logger::ERROR);
|
||||
yield $connection->reconnect();
|
||||
@ -108,18 +102,15 @@ class WriteLoop extends ResumableSignalLoop
|
||||
} finally {
|
||||
$connection->writing(false);
|
||||
}
|
||||
|
||||
//$connection->waiter->resume();
|
||||
}
|
||||
}
|
||||
|
||||
public function unencryptedWriteLoop()
|
||||
public function unencryptedWriteLoop(): \Generator
|
||||
{
|
||||
$API = $this->API;
|
||||
$datacenter = $this->datacenter;
|
||||
$connection = $this->connection;
|
||||
$shared = $this->datacenterConnection;
|
||||
|
||||
while ($connection->pending_outgoing) {
|
||||
$skipped_all = true;
|
||||
foreach ($connection->pending_outgoing as $k => $message) {
|
||||
@ -130,20 +121,14 @@ class WriteLoop extends ResumableSignalLoop
|
||||
continue;
|
||||
}
|
||||
$skipped_all = false;
|
||||
|
||||
$API->logger->logger("Sending {$message['_']} as unencrypted message to DC {$datacenter}", \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
|
||||
$message_id = isset($message['msg_id']) ? $message['msg_id'] : $connection->generateMessageId();
|
||||
$length = \strlen($message['serialized_body']);
|
||||
|
||||
$pad_length = -$length & 15;
|
||||
$pad_length += 16 * \danog\MadelineProto\Tools::randomInt($modulus = 16);
|
||||
|
||||
$pad = \danog\MadelineProto\Tools::random($pad_length);
|
||||
$buffer = yield $connection->stream->getWriteBuffer(8 + 8 + 4 + $pad_length + $length);
|
||||
|
||||
yield $buffer->bufferWrite("\0\0\0\0\0\0\0\0" . $message_id . \danog\MadelineProto\Tools::packUnsignedInt($length) . $message['serialized_body'] . $pad);
|
||||
|
||||
//var_dump("plain ".bin2hex($message_id));
|
||||
$connection->httpSent();
|
||||
$connection->outgoing_messages[$message_id] = $message;
|
||||
@ -151,11 +136,8 @@ class WriteLoop extends ResumableSignalLoop
|
||||
$connection->outgoing_messages[$message_id]['tries'] = 0;
|
||||
$connection->outgoing_messages[$message_id]['unencrypted'] = true;
|
||||
$connection->new_outgoing[$message_id] = $message_id;
|
||||
|
||||
unset($connection->pending_outgoing[$k]);
|
||||
|
||||
$API->logger->logger("Sent {$message['_']} as unencrypted message to DC {$datacenter}!", \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
|
||||
$message['send_promise']->resolve(isset($message['promise']) ? $message['promise'] : true);
|
||||
unset($message['send_promise']);
|
||||
}
|
||||
@ -164,14 +146,12 @@ class WriteLoop extends ResumableSignalLoop
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function encryptedWriteLoop(): \Generator
|
||||
{
|
||||
$API = $this->API;
|
||||
$datacenter = $this->datacenter;
|
||||
$connection = $this->connection;
|
||||
$shared = $this->datacenterConnection;
|
||||
|
||||
do {
|
||||
if (!$shared->hasTempAuthKey()) {
|
||||
return;
|
||||
@ -188,7 +168,6 @@ class WriteLoop extends ResumableSignalLoop
|
||||
$connection->pending_outgoing_key++;
|
||||
}
|
||||
}
|
||||
|
||||
$has_http_wait = false;
|
||||
$messages = [];
|
||||
$keys = [];
|
||||
@ -206,7 +185,6 @@ class WriteLoop extends ResumableSignalLoop
|
||||
$connection->pending_outgoing_key++;
|
||||
}
|
||||
}
|
||||
|
||||
$total_length = 0;
|
||||
$count = 0;
|
||||
\ksort($connection->pending_outgoing);
|
||||
@ -231,45 +209,20 @@ class WriteLoop extends ResumableSignalLoop
|
||||
$API->logger->logger('Length overflow, postponing part of payload', \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
break;
|
||||
}
|
||||
|
||||
$message_id = isset($message['msg_id']) ? $message['msg_id'] : $connection->generateMessageId();
|
||||
|
||||
$API->logger->logger("Sending {$message['_']} as encrypted message to DC {$datacenter}", \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
|
||||
$MTmessage = ['_' => 'MTmessage', 'msg_id' => $message_id, 'body' => $message['serialized_body'], 'seqno' => $connection->generateOutSeqNo($message['contentRelated'])];
|
||||
|
||||
if (isset($message['method']) && $message['method'] && $message['_'] !== 'http_wait') {
|
||||
if (!$shared->getTempAuthKey()->isInited() && $message['_'] !== 'auth.bindTempAuthKey' && !$inited) {
|
||||
$inited = true;
|
||||
$API->logger->logger(\sprintf(\danog\MadelineProto\Lang::$current_lang['write_client_info'], $message['_']), \danog\MadelineProto\Logger::NOTICE);
|
||||
$MTmessage['body'] = yield $API->getTL()->serializeMethod(
|
||||
'invokeWithLayer',
|
||||
[
|
||||
'layer' => $API->settings['tl_schema']['layer'],
|
||||
'query' => yield $API->getTL()->serializeMethod(
|
||||
'initConnection',
|
||||
[
|
||||
'api_id' => $API->settings['app_info']['api_id'],
|
||||
'api_hash' => $API->settings['app_info']['api_hash'],
|
||||
'device_model' => !$connection->isCDN() ? $API->settings['app_info']['device_model'] : 'n/a',
|
||||
'system_version' => !$connection->isCDN() ? $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'],
|
||||
'proxy' => $connection->getCtx()->getInputClientProxy(),
|
||||
'query' => $MTmessage['body'],
|
||||
]
|
||||
),
|
||||
]
|
||||
);
|
||||
$MTmessage['body'] = yield $API->getTL()->serializeMethod('invokeWithLayer', ['layer' => $API->settings['tl_schema']['layer'], 'query' => yield $API->getTL()->serializeMethod('initConnection', ['api_id' => $API->settings['app_info']['api_id'], 'api_hash' => $API->settings['app_info']['api_hash'], 'device_model' => !$connection->isCDN() ? $API->settings['app_info']['device_model'] : 'n/a', 'system_version' => !$connection->isCDN() ? $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'], 'proxy' => $connection->getCtx()->getInputClientProxy(), 'query' => $MTmessage['body']])]);
|
||||
} else {
|
||||
if (isset($message['queue'])) {
|
||||
if (!isset($connection->call_queue[$message['queue']])) {
|
||||
$connection->call_queue[$message['queue']] = [];
|
||||
}
|
||||
$MTmessage['body'] = yield $API->getTL()->serializeMethod('invokeAfterMsgs', ['msg_ids' => $connection->call_queue[$message['queue']], 'query' => $MTmessage['body']]);
|
||||
|
||||
$connection->call_queue[$message['queue']][$message_id] = $message_id;
|
||||
if (\count($connection->call_queue[$message['queue']]) > $API->settings['msg_array_limit']['call_queue']) {
|
||||
\reset($connection->call_queue[$message['queue']]);
|
||||
@ -295,32 +248,25 @@ class WriteLoop extends ResumableSignalLoop
|
||||
}
|
||||
$count++;
|
||||
$total_length += $actual_length;
|
||||
|
||||
$MTmessage['bytes'] = $body_length;
|
||||
$messages[] = $MTmessage;
|
||||
$keys[$k] = $message_id;
|
||||
}
|
||||
if ($shared->isHttp() && $skipped && $count === \count($temporary_keys)) {
|
||||
foreach ($temporary_keys as $key => $true) {
|
||||
$API->logger->logger("Removing temporary {$connection->pending_outgoing[$key]['_']} by $key", Logger::ULTRA_VERBOSE);
|
||||
$API->logger->logger("Removing temporary {$connection->pending_outgoing[$key]['_']} by {$key}", Logger::ULTRA_VERBOSE);
|
||||
unset($connection->pending_outgoing[$key]);
|
||||
$count--;
|
||||
}
|
||||
}
|
||||
|
||||
$MTmessage = null;
|
||||
|
||||
if ($count > 1) {
|
||||
$API->logger->logger("Wrapping in msg_container ($count messages of total size $total_length) as encrypted message for DC {$datacenter}", \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
|
||||
$API->logger->logger("Wrapping in msg_container ({$count} messages of total size {$total_length}) as encrypted message for DC {$datacenter}", \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
$message_id = $connection->generateMessageId();
|
||||
$connection->pending_outgoing[$connection->pending_outgoing_key] = ['_' => 'msg_container', 'container' => \array_values($keys), 'contentRelated' => false, 'method' => false, 'unencrypted' => false];
|
||||
|
||||
//var_dumP("container ".bin2hex($message_id));
|
||||
$keys[$connection->pending_outgoing_key++] = $message_id;
|
||||
|
||||
$message_data = yield $API->getTL()->serializeObject(['type' => ''], ['_' => 'msg_container', 'messages' => $messages], 'container');
|
||||
|
||||
$message_data_length = \strlen($message_data);
|
||||
$seq_no = $connection->generateOutSeqNo(false);
|
||||
} elseif ($count) {
|
||||
@ -330,44 +276,30 @@ class WriteLoop extends ResumableSignalLoop
|
||||
$message_id = $message['msg_id'];
|
||||
$seq_no = $message['seqno'];
|
||||
} else {
|
||||
$API->logger->logger("NO MESSAGE SENT in DC $datacenter", \danog\MadelineProto\Logger::WARNING);
|
||||
|
||||
$API->logger->logger("NO MESSAGE SENT in DC {$datacenter}", \danog\MadelineProto\Logger::WARNING);
|
||||
return true;
|
||||
}
|
||||
|
||||
unset($messages);
|
||||
|
||||
$plaintext = $shared->getTempAuthKey()->getServerSalt() . $connection->session_id . $message_id . \pack('VV', $seq_no, $message_data_length) . $message_data;
|
||||
$padding = \danog\MadelineProto\Tools::posmod(-\strlen($plaintext), 16);
|
||||
if ($padding < 12) {
|
||||
$padding += 16;
|
||||
}
|
||||
$padding = \danog\MadelineProto\Tools::random($padding);
|
||||
|
||||
$message_key = \substr(\hash('sha256', \substr($shared->getTempAuthKey()->getAuthKey(), 88, 32) . $plaintext . $padding, true), 8, 16);
|
||||
|
||||
list($aes_key, $aes_iv) = $this->aesCalculate($message_key, $shared->getTempAuthKey()->getAuthKey());
|
||||
|
||||
$message = $shared->getTempAuthKey()->getID() . $message_key . $this->igeEncrypt($plaintext . $padding, $aes_key, $aes_iv);
|
||||
|
||||
$buffer = yield $connection->stream->getWriteBuffer($len = \strlen($message));
|
||||
|
||||
//$t = \microtime(true);
|
||||
yield $buffer->bufferWrite($message);
|
||||
|
||||
$connection->httpSent();
|
||||
|
||||
$API->logger->logger("Sent encrypted payload to DC {$datacenter}", \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
|
||||
$sent = \time();
|
||||
|
||||
if ($to_ack) {
|
||||
$connection->ack_queue = [];
|
||||
}
|
||||
|
||||
foreach ($keys as $key => $message_id) {
|
||||
$connection->outgoing_messages[$message_id] =& $connection->pending_outgoing[$key];
|
||||
|
||||
if (isset($connection->outgoing_messages[$message_id]['promise'])) {
|
||||
$connection->new_outgoing[$message_id] = $message_id;
|
||||
$connection->outgoing_messages[$message_id]['sent'] = $sent;
|
||||
@ -380,17 +312,13 @@ class WriteLoop extends ResumableSignalLoop
|
||||
//var_dumP("encrypted ".bin2hex($message_id)." ".$connection->outgoing_messages[$message_id]['_']);
|
||||
unset($connection->pending_outgoing[$key]);
|
||||
}
|
||||
|
||||
//if (!empty($connection->pending_outgoing)) $connection->select();
|
||||
} while (!empty($connection->pending_outgoing) && !$skipped);
|
||||
|
||||
if (empty($connection->pending_outgoing)) {
|
||||
$connection->pending_outgoing_key = 'a';
|
||||
}
|
||||
|
||||
return $skipped;
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
return "write loop in DC {$this->datacenter}";
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Generic loop.
|
||||
*
|
||||
@ -30,10 +31,8 @@ class GenericLoop extends ResumableSignalLoop
|
||||
const STOP = -1;
|
||||
const PAUSE = null;
|
||||
const CONTINUE = 0;
|
||||
|
||||
protected $callback;
|
||||
protected $name;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
@ -54,24 +53,21 @@ class GenericLoop extends ResumableSignalLoop
|
||||
$this->callback = $callback->bindTo($this);
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
public function loop()
|
||||
public function loop(): \Generator
|
||||
{
|
||||
$callback = $this->callback;
|
||||
|
||||
while (true) {
|
||||
$timeout = yield $callback();
|
||||
if ($timeout === self::PAUSE) {
|
||||
$this->API->logger->logger("Pausing $this", \danog\MadelineProto\Logger::VERBOSE);
|
||||
$this->API->logger->logger("Pausing {$this}", \danog\MadelineProto\Logger::VERBOSE);
|
||||
} elseif ($timeout > 0) {
|
||||
$this->API->logger->logger("Pausing $this for $timeout", \danog\MadelineProto\Logger::VERBOSE);
|
||||
$this->API->logger->logger("Pausing {$this} for {$timeout}", \danog\MadelineProto\Logger::VERBOSE);
|
||||
}
|
||||
if ($timeout === self::STOP || yield $this->waitSignal($this->pause($timeout))) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
return $this->name;
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Periodic loop.
|
||||
*
|
||||
@ -31,7 +32,6 @@ class PeriodicLoop extends ResumableSignalLoop
|
||||
private $callback;
|
||||
private $name;
|
||||
private $timeout;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
@ -47,22 +47,19 @@ class PeriodicLoop extends ResumableSignalLoop
|
||||
$this->name = $name;
|
||||
$this->timeout = $timeout;
|
||||
}
|
||||
|
||||
public function loop()
|
||||
public function loop(): \Generator
|
||||
{
|
||||
$callback = $this->callback;
|
||||
$logger = $this->API->logger;
|
||||
|
||||
while (true) {
|
||||
$result = yield $this->waitSignal($this->pause($this->timeout));
|
||||
if ($result) {
|
||||
$logger->logger("Got signal in $this, exiting");
|
||||
$logger->logger("Got signal in {$this}, exiting");
|
||||
return;
|
||||
}
|
||||
yield $callback();
|
||||
}
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
return $this->name;
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Loop helper trait.
|
||||
*
|
||||
@ -32,59 +33,49 @@ use danog\MadelineProto\Loop\LoopInterface;
|
||||
abstract class Loop implements LoopInterface
|
||||
{
|
||||
use \danog\MadelineProto\Tools;
|
||||
|
||||
private $count = 0;
|
||||
|
||||
/**
|
||||
* MTProto instance.
|
||||
*
|
||||
* @var \danog\MadelineProto\MTProto
|
||||
*/
|
||||
public $API;
|
||||
|
||||
public function __construct($API)
|
||||
{
|
||||
$this->API = $API;
|
||||
}
|
||||
|
||||
public function start()
|
||||
{
|
||||
if ($this->count) {
|
||||
//$this->API->logger->logger("NOT entering $this with running count {$this->count}", Logger::ERROR);
|
||||
|
||||
return false;
|
||||
}
|
||||
return \danog\MadelineProto\Tools::callFork($this->loopImpl());
|
||||
}
|
||||
|
||||
private function loopImpl()
|
||||
private function loopImpl(): \Generator
|
||||
{
|
||||
//yield ['my_trace' => debug_backtrace(0, 1)[0], (string) $this];
|
||||
$this->startedLoop();
|
||||
$this->API->logger->logger("Entered $this", Logger::ULTRA_VERBOSE);
|
||||
|
||||
$this->API->logger->logger("Entered {$this}", Logger::ULTRA_VERBOSE);
|
||||
try {
|
||||
yield $this->loop();
|
||||
} finally {
|
||||
$this->exitedLoop();
|
||||
$this->API->logger->logger("Physically exited $this", Logger::ULTRA_VERBOSE);
|
||||
$this->API->logger->logger("Physically exited {$this}", Logger::ULTRA_VERBOSE);
|
||||
//return null;
|
||||
}
|
||||
}
|
||||
|
||||
public function exitedLoop()
|
||||
{
|
||||
if ($this->count) {
|
||||
$this->API->logger->logger("Exited $this", Logger::ULTRA_VERBOSE);
|
||||
$this->API->logger->logger("Exited {$this}", Logger::ULTRA_VERBOSE);
|
||||
$this->count--;
|
||||
}
|
||||
}
|
||||
|
||||
public function startedLoop()
|
||||
{
|
||||
$this->count++;
|
||||
}
|
||||
|
||||
public function isRunning()
|
||||
{
|
||||
return $this->count;
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Loop helper trait.
|
||||
*
|
||||
@ -36,7 +37,6 @@ abstract class ResumableSignalLoop extends SignalLoop implements ResumableLoopIn
|
||||
private $resume;
|
||||
private $pause;
|
||||
private $resumeWatcher;
|
||||
|
||||
public function pause($time = null): Promise
|
||||
{
|
||||
if (!\is_null($time)) {
|
||||
@ -51,16 +51,13 @@ abstract class ResumableSignalLoop extends SignalLoop implements ResumableLoopIn
|
||||
$this->resumeWatcher = Loop::delay((int) ($time * 1000), [$this, 'resume'], $resume);
|
||||
}
|
||||
$this->resume = new Deferred();
|
||||
|
||||
$pause = $this->pause;
|
||||
$this->pause = new Deferred();
|
||||
if ($pause) {
|
||||
Loop::defer([$pause, 'resolve']);
|
||||
}
|
||||
|
||||
return $this->resume->promise();
|
||||
}
|
||||
|
||||
public function resume($watcherId = null, $expected = 0)
|
||||
{
|
||||
if ($this->resumeWatcher) {
|
||||
@ -75,15 +72,12 @@ abstract class ResumableSignalLoop extends SignalLoop implements ResumableLoopIn
|
||||
$resume = $this->resume;
|
||||
$this->resume = null;
|
||||
$resume->resolve();
|
||||
|
||||
return $this->pause ? $this->pause->promise() : null;
|
||||
}
|
||||
}
|
||||
|
||||
public function resumeDefer()
|
||||
{
|
||||
Loop::defer([$this, 'resume']);
|
||||
|
||||
return $this->pause ? $this->pause->promise() : null;
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Loop helper trait.
|
||||
*
|
||||
@ -31,7 +32,6 @@ use danog\MadelineProto\Loop\SignalLoopInterface;
|
||||
abstract class SignalLoop extends Loop implements SignalLoopInterface
|
||||
{
|
||||
private $signalDeferred;
|
||||
|
||||
public function signal($what)
|
||||
{
|
||||
if ($this->signalDeferred) {
|
||||
@ -44,7 +44,6 @@ abstract class SignalLoop extends Loop implements SignalLoopInterface
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function waitSignal($promise): Promise
|
||||
{
|
||||
if ($promise instanceof \Generator) {
|
||||
@ -52,7 +51,6 @@ abstract class SignalLoop extends Loop implements SignalLoopInterface
|
||||
}
|
||||
$this->signalDeferred = new Deferred();
|
||||
$dpromise = $this->signalDeferred->promise();
|
||||
|
||||
$promise->onResolve(function () use ($promise) {
|
||||
if ($this->signalDeferred !== null) {
|
||||
$deferred = $this->signalDeferred;
|
||||
@ -60,7 +58,6 @@ abstract class SignalLoop extends Loop implements SignalLoopInterface
|
||||
$deferred->resolve($promise);
|
||||
}
|
||||
});
|
||||
|
||||
return $dpromise;
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Loop interface.
|
||||
*
|
||||
@ -31,14 +32,12 @@ interface LoopInterface
|
||||
* @return void
|
||||
*/
|
||||
public function start();
|
||||
|
||||
/**
|
||||
* The actual loop.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function loop();
|
||||
|
||||
/**
|
||||
* Get name of the loop.
|
||||
*
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Resumable loop interface.
|
||||
*
|
||||
@ -35,7 +36,6 @@ interface ResumableLoopInterface extends LoopInterface
|
||||
* @return Promise
|
||||
*/
|
||||
public function pause($time = null): Promise;
|
||||
|
||||
/**
|
||||
* Resume the loop.
|
||||
*
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Signal loop interface.
|
||||
*
|
||||
@ -35,7 +36,6 @@ interface SignalLoopInterface extends LoopInterface
|
||||
* @return Promise
|
||||
*/
|
||||
public function waitSignal($promise): Promise;
|
||||
|
||||
/**
|
||||
* Send a signal to the the loop.
|
||||
*
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Update feeder loop.
|
||||
*
|
||||
@ -38,29 +39,24 @@ class FeedLoop extends ResumableSignalLoop
|
||||
* @var UpdateLoop
|
||||
*/
|
||||
private $updater;
|
||||
|
||||
public function __construct($API, $channelId = false)
|
||||
{
|
||||
$this->API = $API;
|
||||
$this->channelId = $channelId;
|
||||
}
|
||||
|
||||
public function loop()
|
||||
public function loop(): \Generator
|
||||
{
|
||||
$API = $this->API;
|
||||
$this->updater = $API->updaters[$this->channelId];
|
||||
|
||||
if (!$this->API->settings['updates']['handle_updates']) {
|
||||
return false;
|
||||
}
|
||||
|
||||
while (!$this->API->settings['updates']['handle_updates'] || !$API->hasAllAuth()) {
|
||||
if (yield $this->waitSignal($this->pause())) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
$this->state = $this->channelId === false ? (yield $API->loadUpdateState()) : $API->loadChannelState($this->channelId);
|
||||
|
||||
$this->state = $this->channelId === false ? yield $API->loadUpdateState() : $API->loadChannelState($this->channelId);
|
||||
while (true) {
|
||||
while (!$this->API->settings['updates']['handle_updates'] || !$API->hasAllAuth()) {
|
||||
if (yield $this->waitSignal($this->pause())) {
|
||||
@ -73,11 +69,11 @@ class FeedLoop extends ResumableSignalLoop
|
||||
if (!$this->API->settings['updates']['handle_updates']) {
|
||||
return;
|
||||
}
|
||||
$API->logger->logger("Resumed $this");
|
||||
$API->logger->logger("Resumed {$this}");
|
||||
while ($this->incomingUpdates) {
|
||||
$updates = $this->incomingUpdates;
|
||||
$this->incomingUpdates = [];
|
||||
yield $this->parse($updates);
|
||||
yield from $this->parse($updates);
|
||||
$updates = null;
|
||||
}
|
||||
while ($this->parsedUpdates) {
|
||||
@ -91,8 +87,7 @@ class FeedLoop extends ResumableSignalLoop
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function parse($updates)
|
||||
public function parse($updates): \Generator
|
||||
{
|
||||
\reset($updates);
|
||||
while ($updates) {
|
||||
@ -102,7 +97,6 @@ class FeedLoop extends ResumableSignalLoop
|
||||
if ($update['_'] === 'updateChannelTooLong') {
|
||||
$this->API->logger->logger('Got channel too long update, getting difference...', \danog\MadelineProto\Logger::VERBOSE);
|
||||
yield $this->updater->resume();
|
||||
|
||||
continue;
|
||||
}
|
||||
if (isset($update['pts'], $update['pts_count'])) {
|
||||
@ -111,12 +105,11 @@ class FeedLoop extends ResumableSignalLoop
|
||||
$mid = isset($update['message']['id']) ? $update['message']['id'] : '-';
|
||||
$mypts = $this->state->pts();
|
||||
$computed = $mypts + $pts_count;
|
||||
$this->API->logger->logger("$msg. My pts: {$mypts}, remote pts: {$update['pts']}, computed pts: $computed, msg id: {$mid}, channel id: {$this->channelId}", \danog\MadelineProto\Logger::ERROR);
|
||||
$this->API->logger->logger("{$msg}. My pts: {$mypts}, remote pts: {$update['pts']}, computed pts: {$computed}, msg id: {$mid}, channel id: {$this->channelId}", \danog\MadelineProto\Logger::ERROR);
|
||||
};
|
||||
$result = $this->state->checkPts($update);
|
||||
if ($result < 0) {
|
||||
$logger('PTS duplicate');
|
||||
|
||||
continue;
|
||||
}
|
||||
if ($result > 0) {
|
||||
@ -130,19 +123,16 @@ class FeedLoop extends ResumableSignalLoop
|
||||
if (isset($update['message']['id'], $update['message']['to_id']) && !\in_array($update['_'], ['updateEditMessage', 'updateEditChannelMessage', 'updateMessageID'])) {
|
||||
if (!$this->API->checkMsgId($update['message'])) {
|
||||
$logger('MSGID duplicate');
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
$logger('PTS OK');
|
||||
|
||||
$this->state->pts($update['pts']);
|
||||
}
|
||||
$this->save($update);
|
||||
}
|
||||
}
|
||||
|
||||
public function feed($updates)
|
||||
public function feed($updates): \Generator
|
||||
{
|
||||
$result = [];
|
||||
foreach ($updates as $update) {
|
||||
@ -152,11 +142,9 @@ class FeedLoop extends ResumableSignalLoop
|
||||
}
|
||||
$result[$res] = true;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function feedSingle($update)
|
||||
public function feedSingle($update): \Generator
|
||||
{
|
||||
$channelId = false;
|
||||
switch ($update['_']) {
|
||||
@ -178,7 +166,6 @@ class FeedLoop extends ResumableSignalLoop
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if ($channelId && !$this->API->getChannelStates()->has($channelId)) {
|
||||
$this->API->loadChannelState($channelId, $update);
|
||||
if (!isset($this->API->feeders[$channelId])) {
|
||||
@ -190,7 +177,6 @@ class FeedLoop extends ResumableSignalLoop
|
||||
$this->API->feeders[$channelId]->start();
|
||||
$this->API->updaters[$channelId]->start();
|
||||
}
|
||||
|
||||
switch ($update['_']) {
|
||||
case 'updateNewMessage':
|
||||
case 'updateEditMessage':
|
||||
@ -200,31 +186,21 @@ class FeedLoop extends ResumableSignalLoop
|
||||
$from = false;
|
||||
$via_bot = false;
|
||||
$entities = false;
|
||||
if ($update['message']['_'] !== 'messageEmpty' && (
|
||||
($from = isset($update['message']['from_id']) && !yield $this->API->peerIsset($update['message']['from_id'])) ||
|
||||
($to = !yield $this->API->peerIsset($update['message']['to_id'])) ||
|
||||
($via_bot = isset($update['message']['via_bot_id']) && !yield $this->API->peerIsset($update['message']['via_bot_id'])) ||
|
||||
($entities = isset($update['message']['entities']) && !yield $this->API->entitiesPeerIsset($update['message']['entities'])) // ||
|
||||
//isset($update['message']['fwd_from']) && !yield $this->fwdPeerIsset($update['message']['fwd_from'])
|
||||
)) {
|
||||
if ($update['message']['_'] !== 'messageEmpty' && (($from = isset($update['message']['from_id']) && !yield $this->API->peerIsset($update['message']['from_id'])) || ($to = !yield $this->API->peerIsset($update['message']['to_id'])) || ($via_bot = isset($update['message']['via_bot_id']) && !yield $this->API->peerIsset($update['message']['via_bot_id'])) || ($entities = isset($update['message']['entities']) && !yield $this->API->entitiesPeerIsset($update['message']['entities'])))) {
|
||||
$log = '';
|
||||
if ($from) {
|
||||
$log .= "from_id {$update['message']['from_id']}, ";
|
||||
}
|
||||
|
||||
if ($to) {
|
||||
$log .= 'to_id ' . \json_encode($update['message']['to_id']) . ', ';
|
||||
}
|
||||
|
||||
if ($via_bot) {
|
||||
$log .= "via_bot {$update['message']['via_bot_id']}, ";
|
||||
}
|
||||
|
||||
if ($entities) {
|
||||
$log .= 'entities ' . \json_encode($update['message']['entities']) . ', ';
|
||||
}
|
||||
|
||||
$this->API->logger->logger("Not enough data: for message update $log, getting difference...", \danog\MadelineProto\Logger::VERBOSE);
|
||||
$this->API->logger->logger("Not enough data: for message update {$log}, getting difference...", \danog\MadelineProto\Logger::VERBOSE);
|
||||
$update = ['_' => 'updateChannelTooLong'];
|
||||
if ($channelId && $to) {
|
||||
$channelId = false;
|
||||
@ -234,46 +210,38 @@ class FeedLoop extends ResumableSignalLoop
|
||||
default:
|
||||
if ($channelId && !yield $this->API->peerIsset($this->API->toSupergroup($channelId))) {
|
||||
$this->API->logger->logger('Skipping update, I do not have the channel id ' . $channelId, \danog\MadelineProto\Logger::ERROR);
|
||||
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if ($channelId !== $this->channelId) {
|
||||
if (isset($this->API->feeders[$channelId])) {
|
||||
return yield $this->API->feeders[$channelId]->feedSingle($update);
|
||||
return yield from $this->API->feeders[$channelId]->feedSingle($update);
|
||||
} elseif ($this->channelId) {
|
||||
return yield $this->API->feeders[false]->feedSingle($update);
|
||||
return yield from $this->API->feeders[false]->feedSingle($update);
|
||||
}
|
||||
}
|
||||
|
||||
$this->API->logger->logger('Was fed an update of type '.$update['_']." in $this...", \danog\MadelineProto\Logger::VERBOSE);
|
||||
$this->API->logger->logger('Was fed an update of type ' . $update['_'] . " in {$this}...", \danog\MadelineProto\Logger::VERBOSE);
|
||||
$this->incomingUpdates[] = $update;
|
||||
|
||||
return $this->channelId;
|
||||
}
|
||||
|
||||
public function save($update)
|
||||
{
|
||||
$this->parsedUpdates[] = $update;
|
||||
}
|
||||
|
||||
public function saveMessages($messages)
|
||||
{
|
||||
foreach ($messages as $message) {
|
||||
if (!$this->API->checkMsgId($message)) {
|
||||
$this->API->logger->logger("MSGID duplicate ({$message['id']}) in $this");
|
||||
|
||||
$this->API->logger->logger("MSGID duplicate ({$message['id']}) in {$this}");
|
||||
continue;
|
||||
}
|
||||
if ($message['_'] !== 'messageEmpty') {
|
||||
$this->API->logger->logger('Getdiff fed me message of type '.$message['_']." in $this...", \danog\MadelineProto\Logger::VERBOSE);
|
||||
$this->API->logger->logger('Getdiff fed me message of type ' . $message['_'] . " in {$this}...", \danog\MadelineProto\Logger::VERBOSE);
|
||||
}
|
||||
|
||||
$this->parsedUpdates[] = ['_' => $this->channelId === false ? 'updateNewMessage' : 'updateNewChannelMessage', 'message' => $message, 'pts' => -1, 'pts_count' => -1];
|
||||
}
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
return !$this->channelId ? 'update feed loop generic' : "update feed loop channel {$this->channelId}";
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Update feeder loop.
|
||||
*
|
||||
@ -31,28 +32,23 @@ class SeqLoop extends ResumableSignalLoop
|
||||
private $incomingUpdates = [];
|
||||
private $feeder;
|
||||
private $pendingWakeups = [];
|
||||
|
||||
public function __construct($API)
|
||||
{
|
||||
$this->API = $API;
|
||||
}
|
||||
|
||||
public function loop()
|
||||
public function loop(): \Generator
|
||||
{
|
||||
$API = $this->API;
|
||||
$this->feeder = $API->feeders[false];
|
||||
|
||||
if (!$this->API->settings['updates']['handle_updates']) {
|
||||
return false;
|
||||
}
|
||||
|
||||
while (!$this->API->settings['updates']['handle_updates'] || !$API->hasAllAuth()) {
|
||||
if (yield $this->waitSignal($this->pause())) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
$this->state = yield $API->loadUpdateState();
|
||||
|
||||
while (true) {
|
||||
while (!$this->API->settings['updates']['handle_updates'] || !$API->hasAllAuth()) {
|
||||
if (yield $this->waitSignal($this->pause())) {
|
||||
@ -68,7 +64,7 @@ class SeqLoop extends ResumableSignalLoop
|
||||
while ($this->incomingUpdates) {
|
||||
$updates = $this->incomingUpdates;
|
||||
$this->incomingUpdates = [];
|
||||
yield $this->parse($updates);
|
||||
yield from $this->parse($updates);
|
||||
$updates = null;
|
||||
}
|
||||
while ($this->pendingWakeups) {
|
||||
@ -79,8 +75,7 @@ class SeqLoop extends ResumableSignalLoop
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function parse($updates)
|
||||
public function parse($updates): \Generator
|
||||
{
|
||||
\reset($updates);
|
||||
while ($updates) {
|
||||
@ -88,11 +83,9 @@ class SeqLoop extends ResumableSignalLoop
|
||||
$key = \key($updates);
|
||||
$update = $updates[$key];
|
||||
unset($updates[$key]);
|
||||
|
||||
$options = $update['options'];
|
||||
$seq_start = $options['seq_start'];
|
||||
$seq_end = $options['seq_end'];
|
||||
|
||||
$result = $this->state->checkSeq($seq_start);
|
||||
if ($result > 0) {
|
||||
$this->API->logger->logger('Seq hole. seq_start: ' . $seq_start . ' != cur seq: ' . ($this->state->seq() + 1), \danog\MadelineProto\Logger::ERROR);
|
||||
@ -101,7 +94,6 @@ class SeqLoop extends ResumableSignalLoop
|
||||
yield $this->API->updaters[false]->resume();
|
||||
}
|
||||
$this->incomingUpdates = \array_merge($this->incomingUpdates, [$update], $updates);
|
||||
|
||||
continue;
|
||||
}
|
||||
if ($result < 0) {
|
||||
@ -112,29 +104,22 @@ class SeqLoop extends ResumableSignalLoop
|
||||
if (isset($options['date'])) {
|
||||
$this->state->date($options['date']);
|
||||
}
|
||||
|
||||
yield $this->save($update);
|
||||
yield from $this->save($update);
|
||||
}
|
||||
}
|
||||
|
||||
public function feed($updates)
|
||||
{
|
||||
$this->API->logger->logger('Was fed updates of type ' . $updates['_'] . '...', \danog\MadelineProto\Logger::VERBOSE);
|
||||
|
||||
$this->incomingUpdates[] = $updates;
|
||||
}
|
||||
|
||||
public function save($updates)
|
||||
public function save($updates): \Generator
|
||||
{
|
||||
$this->pendingWakeups += yield $this->feeder->feed($updates['updates']);
|
||||
}
|
||||
|
||||
public function addPendingWakeups($wakeups)
|
||||
{
|
||||
$this->pendingWakeups += $wakeups;
|
||||
}
|
||||
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
return 'update seq loop';
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Update loop.
|
||||
*
|
||||
@ -31,38 +32,31 @@ use danog\MadelineProto\RPCErrorException;
|
||||
class UpdateLoop extends ResumableSignalLoop
|
||||
{
|
||||
use \danog\MadelineProto\Tools;
|
||||
|
||||
private $toPts;
|
||||
private $channelId;
|
||||
private $feeder;
|
||||
|
||||
public function __construct($API, $channelId)
|
||||
{
|
||||
$this->API = $API;
|
||||
$this->channelId = $channelId;
|
||||
}
|
||||
|
||||
public function loop()
|
||||
public function loop(): \Generator
|
||||
{
|
||||
$API = $this->API;
|
||||
$feeder = $this->feeder = $API->feeders[$this->channelId];
|
||||
|
||||
while (!$API->settings['updates']['handle_updates'] || !$API->hasAllAuth()) {
|
||||
if (yield $this->waitSignal($this->pause())) {
|
||||
$API->logger->logger("Exiting $this due to signal");
|
||||
|
||||
$API->logger->logger("Exiting {$this} due to signal");
|
||||
return;
|
||||
}
|
||||
}
|
||||
$this->state = $state = $this->channelId === false ? (yield $API->loadUpdateState()) : $API->loadChannelState($this->channelId);
|
||||
|
||||
$this->state = $state = $this->channelId === false ? yield $API->loadUpdateState() : $API->loadChannelState($this->channelId);
|
||||
$timeout = $API->settings['updates']['getdifference_interval'];
|
||||
$first = true;
|
||||
while (true) {
|
||||
while (!$API->settings['updates']['handle_updates'] || !$API->hasAllAuth()) {
|
||||
if (yield $this->waitSignal($this->pause())) {
|
||||
$API->logger->logger("Exiting $this due to signal");
|
||||
|
||||
$API->logger->logger("Exiting {$this} due to signal");
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -80,17 +74,14 @@ class UpdateLoop extends ResumableSignalLoop
|
||||
$limit = 100;
|
||||
}
|
||||
$request_pts = $state->pts();
|
||||
|
||||
try {
|
||||
$difference = yield $API->methodCallAsyncRead('updates.getChannelDifference', ['channel' => 'channel#' . $this->channelId, 'filter' => ['_' => 'channelMessagesFilterEmpty'], 'pts' => $request_pts, 'limit' => $limit, 'force' => true], ['datacenter' => $API->datacenter->curdc, 'postpone' => $first]);
|
||||
} catch (RPCErrorException $e) {
|
||||
if (\in_array($e->rpc, ['CHANNEL_PRIVATE', 'CHAT_FORBIDDEN', 'CHANNEL_INVALID'])) {
|
||||
$feeder->signal(true);
|
||||
unset($API->updaters[$this->channelId], $API->feeders[$this->channelId]);
|
||||
|
||||
$API->getChannelStates()->remove($this->channelId);
|
||||
$API->logger->logger("Channel private, exiting $this");
|
||||
|
||||
$API->logger->logger("Channel private, exiting {$this}");
|
||||
return true;
|
||||
}
|
||||
throw $e;
|
||||
@ -99,9 +90,7 @@ class UpdateLoop extends ResumableSignalLoop
|
||||
$feeder->signal(true);
|
||||
$API->getChannelStates()->remove($this->channelId);
|
||||
unset($API->updaters[$this->channelId], $API->feeders[$this->channelId]);
|
||||
|
||||
$API->logger->logger("Channel private, exiting $this");
|
||||
|
||||
$API->logger->logger("Channel private, exiting {$this}");
|
||||
return true;
|
||||
}
|
||||
throw $e;
|
||||
@ -109,7 +98,6 @@ class UpdateLoop extends ResumableSignalLoop
|
||||
if (isset($difference['timeout'])) {
|
||||
$timeout = $difference['timeout'];
|
||||
}
|
||||
|
||||
$API->logger->logger('Got ' . $difference['_'], \danog\MadelineProto\Logger::VERBOSE);
|
||||
switch ($difference['_']) {
|
||||
case 'updates.channelDifferenceEmpty':
|
||||
@ -123,7 +111,6 @@ class UpdateLoop extends ResumableSignalLoop
|
||||
}
|
||||
$result += yield $feeder->feed($difference['other_updates']);
|
||||
$state->update($difference);
|
||||
|
||||
$feeder->saveMessages($difference['new_messages']);
|
||||
if (!$difference['final']) {
|
||||
if ($difference['pts'] >= $toPts) {
|
||||
@ -148,7 +135,6 @@ class UpdateLoop extends ResumableSignalLoop
|
||||
}
|
||||
} else {
|
||||
$API->logger->logger('Resumed and fetching normal difference...', \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
|
||||
$difference = yield $API->methodCallAsyncRead('updates.getDifference', ['pts' => $state->pts(), 'date' => $state->date(), 'qts' => $state->qts()], ['datacenter' => $API->settings['connection_settings']['default_dc']]);
|
||||
$API->logger->logger('Got ' . $difference['_'], \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
switch ($difference['_']) {
|
||||
@ -191,28 +177,24 @@ class UpdateLoop extends ResumableSignalLoop
|
||||
}
|
||||
}
|
||||
}
|
||||
$API->logger->logger("Finished parsing updates in $this, now resuming feeders");
|
||||
$API->logger->logger("Finished parsing updates in {$this}, now resuming feeders");
|
||||
foreach ($result as $channelId => $boh) {
|
||||
$API->feeders[$channelId]->resumeDefer();
|
||||
}
|
||||
$API->logger->logger("Finished resuming feeders in $this, signaling updates");
|
||||
$API->logger->logger("Finished resuming feeders in {$this}, signaling updates");
|
||||
$API->signalUpdate();
|
||||
$API->logger->logger("Finished signaling updates in $this, pausing");
|
||||
$API->logger->logger("Finished signaling updates in {$this}, pausing");
|
||||
$first = false;
|
||||
|
||||
if (yield $this->waitSignal($this->pause($timeout))) {
|
||||
$API->logger->logger("Exiting $this due to signal");
|
||||
|
||||
$API->logger->logger("Exiting {$this} due to signal");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function setLimit($toPts)
|
||||
{
|
||||
$this->toPts = $toPts;
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
return !$this->channelId ? 'getUpdate loop generic' : "getUpdate loop channel {$this->channelId}";
|
||||
|
@ -25,7 +25,6 @@ class Lua
|
||||
public $MadelineProto;
|
||||
protected $Lua;
|
||||
protected $script;
|
||||
|
||||
public function __magic_construct($script, $MadelineProto)
|
||||
{
|
||||
if (!\file_exists($script)) {
|
||||
@ -34,16 +33,13 @@ class Lua
|
||||
$this->MadelineProto = $MadelineProto;
|
||||
$this->MadelineProto->settings['updates']['handle_updates'] = true;
|
||||
$this->MadelineProto->API->datacenter->sockets[$this->MadelineProto->settings['connection_settings']['default_dc']]->startUpdateLoop();
|
||||
|
||||
$this->script = $script;
|
||||
$this->__wakeup();
|
||||
}
|
||||
|
||||
public function __sleep()
|
||||
{
|
||||
return ['MadelineProto', 'script'];
|
||||
}
|
||||
|
||||
public function __wakeup()
|
||||
{
|
||||
$this->Lua = new \Lua($this->script);
|
||||
@ -77,7 +73,6 @@ class Lua
|
||||
$this->MadelineProto->{$namespace}->lua = true;
|
||||
}
|
||||
}
|
||||
|
||||
public function tdcliFunction($params, $cb = null, $cb_extra = null)
|
||||
{
|
||||
$params = $this->MadelineProto->td_to_mtproto($this->MadelineProto->tdcliToTd($params));
|
||||
@ -88,10 +83,8 @@ class Lua
|
||||
if (\is_callable($cb)) {
|
||||
$cb($this->MadelineProto->mtproto_to_td($result), $cb_extra);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function madelineFunction($params, $cb = null, $cb_extra = null)
|
||||
{
|
||||
$result = $this->MadelineProto->API->methodCall($params['_'], $params, ['datacenter' => $this->MadelineProto->API->datacenter->curdc]);
|
||||
@ -99,15 +92,12 @@ class Lua
|
||||
$cb($result, $cb_extra);
|
||||
}
|
||||
self::convertObjects($result);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function tdcliUpdateCallback($update)
|
||||
{
|
||||
$this->Lua->tdcliUpdateCallback($this->MadelineProto->mtproto_to_tdcli($update));
|
||||
}
|
||||
|
||||
private function convertArray($array)
|
||||
{
|
||||
if (!\is_array($array)) {
|
||||
@ -119,29 +109,23 @@ class Lua
|
||||
}, \array_flip($array)));
|
||||
}
|
||||
}
|
||||
|
||||
private function isSequential(array $arr)
|
||||
{
|
||||
if ([] === $arr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return isset($arr[0]) && \array_keys($arr) === \range(0, \count($arr) - 1);
|
||||
}
|
||||
|
||||
public function __get($name)
|
||||
{
|
||||
if ($name === 'API') {
|
||||
return $this->MadelineProto->API;
|
||||
}
|
||||
|
||||
return $this->Lua->{$name};
|
||||
}
|
||||
|
||||
public function __call($name, $params)
|
||||
{
|
||||
self::convertObjects($params);
|
||||
|
||||
try {
|
||||
return $this->Lua->{$name}(...$params);
|
||||
} catch (\danog\MadelineProto\RPCErrorException $e) {
|
||||
@ -160,12 +144,10 @@ class Lua
|
||||
return ['error_code' => $e->getCode(), 'error' => $e->getMessage()];
|
||||
}
|
||||
}
|
||||
|
||||
public function __set($name, $value)
|
||||
{
|
||||
return $this->Lua->{$name} = $value;
|
||||
}
|
||||
|
||||
public static function convertObjects(&$data)
|
||||
{
|
||||
\array_walk_recursive($data, function (&$value, $key) {
|
||||
|
File diff suppressed because one or more lines are too long
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* MTProto Auth key.
|
||||
*
|
||||
@ -43,7 +44,6 @@ abstract class AuthKey implements JsonSerializable
|
||||
* @var string
|
||||
*/
|
||||
protected $serverSalt;
|
||||
|
||||
/**
|
||||
* Constructor function.
|
||||
*
|
||||
@ -61,8 +61,6 @@ abstract class AuthKey implements JsonSerializable
|
||||
$this->setServerSalt($old['server_salt']);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set auth key.
|
||||
*
|
||||
@ -75,7 +73,6 @@ abstract class AuthKey implements JsonSerializable
|
||||
$this->authKey = $authKey;
|
||||
$this->id = \substr(\sha1($authKey, true), -8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if auth key is present.
|
||||
*
|
||||
@ -85,7 +82,6 @@ abstract class AuthKey implements JsonSerializable
|
||||
{
|
||||
return $this->authKey !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get auth key.
|
||||
*
|
||||
@ -95,7 +91,6 @@ abstract class AuthKey implements JsonSerializable
|
||||
{
|
||||
return $this->authKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get auth key ID.
|
||||
*
|
||||
@ -105,7 +100,6 @@ abstract class AuthKey implements JsonSerializable
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set server salt.
|
||||
*
|
||||
@ -117,7 +111,6 @@ abstract class AuthKey implements JsonSerializable
|
||||
{
|
||||
$this->serverSalt = $salt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get server salt.
|
||||
*
|
||||
@ -127,7 +120,6 @@ abstract class AuthKey implements JsonSerializable
|
||||
{
|
||||
return $this->serverSalt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if has server salt.
|
||||
*
|
||||
@ -137,14 +129,12 @@ abstract class AuthKey implements JsonSerializable
|
||||
{
|
||||
return $this->serverSalt !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if we are logged in.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
abstract public function isAuthorized(): bool;
|
||||
|
||||
/**
|
||||
* Set the authorized boolean.
|
||||
*
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* MTProto permanent auth key.
|
||||
*
|
||||
@ -29,7 +30,6 @@ class PermAuthKey extends AuthKey
|
||||
* @var boolean
|
||||
*/
|
||||
private $authorized = false;
|
||||
|
||||
/**
|
||||
* Constructor function.
|
||||
*
|
||||
@ -51,7 +51,6 @@ class PermAuthKey extends AuthKey
|
||||
{
|
||||
return $this->authorized;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the authorized boolean.
|
||||
*
|
||||
@ -63,8 +62,6 @@ class PermAuthKey extends AuthKey
|
||||
{
|
||||
$this->authorized = $authorized;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* JSON serialization function.
|
||||
*
|
||||
@ -72,11 +69,7 @@ class PermAuthKey extends AuthKey
|
||||
*/
|
||||
public function jsonSerialize(): array
|
||||
{
|
||||
return [
|
||||
'auth_key' => 'pony'.\base64_encode($this->authKey),
|
||||
'server_salt' => $this->serverSalt,
|
||||
'authorized' => $this->authorized
|
||||
];
|
||||
return ['auth_key' => 'pony' . \base64_encode($this->authKey), 'server_salt' => $this->serverSalt, 'authorized' => $this->authorized];
|
||||
}
|
||||
/**
|
||||
* Sleep function.
|
||||
@ -85,11 +78,6 @@ class PermAuthKey extends AuthKey
|
||||
*/
|
||||
public function __sleep()
|
||||
{
|
||||
return [
|
||||
'authKey',
|
||||
'id',
|
||||
'serverSalt',
|
||||
'authorized'
|
||||
];
|
||||
return ['authKey', 'id', 'serverSalt', 'authorized'];
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* MTProto temporary auth key.
|
||||
*
|
||||
@ -31,21 +32,18 @@ class TempAuthKey extends AuthKey implements JsonSerializable
|
||||
* @var PermAuthKey|null
|
||||
*/
|
||||
private $bound;
|
||||
|
||||
/**
|
||||
* Expiration date.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $expires = 0;
|
||||
|
||||
/**
|
||||
* Whether the connection is inited for this auth key.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
protected $inited = false;
|
||||
|
||||
/**
|
||||
* Constructor function.
|
||||
*
|
||||
@ -61,7 +59,6 @@ class TempAuthKey extends AuthKey implements JsonSerializable
|
||||
$this->init($old['connection_inited']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Init or deinit connection for auth key.
|
||||
*
|
||||
@ -82,7 +79,6 @@ class TempAuthKey extends AuthKey implements JsonSerializable
|
||||
{
|
||||
return $this->inited;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind auth key.
|
||||
*
|
||||
@ -100,7 +96,6 @@ class TempAuthKey extends AuthKey implements JsonSerializable
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if auth key is bound.
|
||||
*
|
||||
@ -110,7 +105,6 @@ class TempAuthKey extends AuthKey implements JsonSerializable
|
||||
{
|
||||
return $this->bound !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if we are logged in.
|
||||
*
|
||||
@ -120,7 +114,6 @@ class TempAuthKey extends AuthKey implements JsonSerializable
|
||||
{
|
||||
return $this->bound ? $this->bound->isAuthorized() : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the authorized boolean.
|
||||
*
|
||||
@ -132,7 +125,6 @@ class TempAuthKey extends AuthKey implements JsonSerializable
|
||||
{
|
||||
$this->bound->authorized($authorized);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set expiration date of temporary auth key.
|
||||
*
|
||||
@ -144,7 +136,6 @@ class TempAuthKey extends AuthKey implements JsonSerializable
|
||||
{
|
||||
$this->expires = $expires;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if auth key has expired.
|
||||
*
|
||||
@ -154,7 +145,6 @@ class TempAuthKey extends AuthKey implements JsonSerializable
|
||||
{
|
||||
return \time() > $this->expires;
|
||||
}
|
||||
|
||||
/**
|
||||
* JSON serialization function.
|
||||
*
|
||||
@ -162,15 +152,8 @@ class TempAuthKey extends AuthKey implements JsonSerializable
|
||||
*/
|
||||
public function jsonSerialize(): array
|
||||
{
|
||||
return [
|
||||
'auth_key' => 'pony'.\base64_encode($this->authKey),
|
||||
'server_salt' => $this->serverSalt,
|
||||
'bound' => $this->isBound(),
|
||||
'expires' => $this->expires,
|
||||
'connection_inited' => $this->inited
|
||||
];
|
||||
return ['auth_key' => 'pony' . \base64_encode($this->authKey), 'server_salt' => $this->serverSalt, 'bound' => $this->isBound(), 'expires' => $this->expires, 'connection_inited' => $this->inited];
|
||||
}
|
||||
|
||||
/**
|
||||
* Sleep function.
|
||||
*
|
||||
@ -178,14 +161,7 @@ class TempAuthKey extends AuthKey implements JsonSerializable
|
||||
*/
|
||||
public function __sleep()
|
||||
{
|
||||
return [
|
||||
'authKey',
|
||||
'id',
|
||||
'serverSalt',
|
||||
'bound',
|
||||
'expires',
|
||||
'inited'
|
||||
];
|
||||
return ['authKey', 'id', 'serverSalt', 'bound', 'expires', 'inited'];
|
||||
}
|
||||
/**
|
||||
* Wakeup function.
|
||||
|
@ -29,7 +29,6 @@ trait AckHandler
|
||||
// The server acknowledges that it received my message
|
||||
if (!isset($this->outgoing_messages[$message_id])) {
|
||||
$this->logger->logger("WARNING: Couldn't find message id " . $message_id . ' in the array of outgoing messages. Maybe try to increase its size?', \danog\MadelineProto\Logger::WARNING);
|
||||
|
||||
return false;
|
||||
}
|
||||
//$this->logger->logger("Ack-ed ".$this->outgoing_messages[$message_id]['_']." with message ID $message_id on DC $datacenter");
|
||||
@ -42,17 +41,14 @@ trait AckHandler
|
||||
}*/
|
||||
return true;
|
||||
}
|
||||
|
||||
public function gotResponseForOutgoingMessageId($message_id): bool
|
||||
{
|
||||
// The server acknowledges that it received my message
|
||||
if (isset($this->new_outgoing[$message_id])) {
|
||||
unset($this->new_outgoing[$message_id]);
|
||||
}
|
||||
|
||||
if (!isset($this->outgoing_messages[$message_id])) {
|
||||
$this->logger->logger("WARNING: Couldn't find message id " . $message_id . ' in the array of outgoing messages. Maybe try to increase its size?', \danog\MadelineProto\Logger::WARNING);
|
||||
|
||||
return false;
|
||||
}
|
||||
if (isset($this->outgoing_messages[$message_id]['body'])) {
|
||||
@ -61,10 +57,8 @@ trait AckHandler
|
||||
if (isset($this->outgoing_messages[$message_id]['serialized_body'])) {
|
||||
unset($this->outgoing_messages[$message_id]['serialized_body']);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function ackIncomingMessageId($message_id): bool
|
||||
{
|
||||
// I let the server know that I received its message
|
||||
@ -76,13 +70,8 @@ trait AckHandler
|
||||
return;
|
||||
}*/
|
||||
$this->ack_queue[$message_id] = $message_id;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Check if there are some pending calls.
|
||||
*
|
||||
@ -95,26 +84,17 @@ trait AckHandler
|
||||
$pfs = $settings['pfs'];
|
||||
$unencrypted = !$this->shared->hasTempAuthKey();
|
||||
$notBound = !$this->shared->isBound();
|
||||
|
||||
$pfsNotBound = $pfs && $notBound;
|
||||
|
||||
foreach ($this->new_outgoing as $message_id) {
|
||||
if (isset($this->outgoing_messages[$message_id]['sent'])
|
||||
&& $this->outgoing_messages[$message_id]['sent'] + $timeout < \time()
|
||||
&& $unencrypted === $this->outgoing_messages[$message_id]['unencrypted']
|
||||
&& $this->outgoing_messages[$message_id]['_'] !== 'msgs_state_req'
|
||||
) {
|
||||
if (isset($this->outgoing_messages[$message_id]['sent']) && $this->outgoing_messages[$message_id]['sent'] + $timeout < \time() && $unencrypted === $this->outgoing_messages[$message_id]['unencrypted'] && $this->outgoing_messages[$message_id]['_'] !== 'msgs_state_req') {
|
||||
if ($pfsNotBound && $this->outgoing_messages[$message_id]['_'] !== 'auth.bindTempAuthKey') {
|
||||
continue;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all pending calls (also clear pending state requests).
|
||||
*
|
||||
@ -127,28 +107,20 @@ trait AckHandler
|
||||
$pfs = $settings['pfs'];
|
||||
$unencrypted = !$this->shared->hasTempAuthKey();
|
||||
$notBound = !$this->shared->isBound();
|
||||
|
||||
$pfsNotBound = $pfs && $notBound;
|
||||
|
||||
$result = [];
|
||||
foreach ($this->new_outgoing as $k => $message_id) {
|
||||
if (isset($this->outgoing_messages[$message_id]['sent'])
|
||||
&& $this->outgoing_messages[$message_id]['sent'] + $timeout < \time()
|
||||
&& $unencrypted === $this->outgoing_messages[$message_id]['unencrypted']
|
||||
) {
|
||||
if (isset($this->outgoing_messages[$message_id]['sent']) && $this->outgoing_messages[$message_id]['sent'] + $timeout < \time() && $unencrypted === $this->outgoing_messages[$message_id]['unencrypted']) {
|
||||
if ($pfsNotBound && $this->outgoing_messages[$message_id]['_'] !== 'auth.bindTempAuthKey') {
|
||||
continue;
|
||||
}
|
||||
if ($this->outgoing_messages[$message_id]['_'] === 'msgs_state_req') {
|
||||
unset($this->new_outgoing[$k], $this->outgoing_messages[$message_id]);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$result[] = $message_id;
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
@ -24,7 +24,6 @@ use Amp\Promise;
|
||||
use Amp\Success;
|
||||
use danog\MadelineProto\Async\AsyncParameters;
|
||||
use danog\MadelineProto\Tools;
|
||||
|
||||
use function Amp\Promise\all;
|
||||
|
||||
/**
|
||||
@ -48,17 +47,13 @@ trait CallHandler
|
||||
if ($datacenter === $this->datacenter) {
|
||||
$datacenter = false;
|
||||
}
|
||||
|
||||
$message_ids = $this->outgoing_messages[$message_id]['container'] ?? [$message_id];
|
||||
|
||||
foreach ($message_ids as $message_id) {
|
||||
if (isset($this->outgoing_messages[$message_id]['body'])) {
|
||||
if ($datacenter) {
|
||||
$res = $this->API->datacenter->waitGetConnection($datacenter)->onResolve(
|
||||
function ($e, $r) use ($message_id) {
|
||||
$res = $this->API->datacenter->waitGetConnection($datacenter)->onResolve(function ($e, $r) use ($message_id) {
|
||||
return $r->sendMessage($this->outgoing_messages[$message_id], false);
|
||||
}
|
||||
);
|
||||
});
|
||||
} else {
|
||||
$res = $this->sendMessage($this->outgoing_messages[$message_id], false);
|
||||
}
|
||||
@ -77,7 +72,6 @@ trait CallHandler
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Call method and wait asynchronously for response.
|
||||
*
|
||||
@ -97,22 +91,17 @@ trait CallHandler
|
||||
$deferred->fail($e);
|
||||
} else {
|
||||
if (\is_array($read_deferred)) {
|
||||
$read_deferred = \array_map(
|
||||
function ($value) {
|
||||
$read_deferred = \array_map(function ($value) {
|
||||
return $value->promise();
|
||||
},
|
||||
$read_deferred
|
||||
);
|
||||
}, $read_deferred);
|
||||
$deferred->resolve(all($read_deferred));
|
||||
} else {
|
||||
$deferred->resolve($read_deferred->promise());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return ($aargs['noResponse'] ?? false) ? new Success() : $deferred->promise();
|
||||
return $aargs['noResponse'] ?? false ? new Success() : $deferred->promise();
|
||||
}
|
||||
|
||||
/**
|
||||
* Call method and make sure it is asynchronously sent.
|
||||
*
|
||||
@ -126,7 +115,6 @@ trait CallHandler
|
||||
{
|
||||
return \danog\MadelineProto\Tools::call($this->methodCallAsyncWriteGenerator($method, $args, $aargs));
|
||||
}
|
||||
|
||||
/**
|
||||
* Call method and make sure it is asynchronously sent (generator).
|
||||
*
|
||||
@ -138,12 +126,7 @@ trait CallHandler
|
||||
*/
|
||||
public function methodCallAsyncWriteGenerator(string $method, $args = [], array $aargs = ['msg_id' => null]): \Generator
|
||||
{
|
||||
if (\is_array($args)
|
||||
&& isset($args['id']['_'])
|
||||
&& isset($args['id']['dc_id'])
|
||||
&& $args['id']['_'] === 'inputBotInlineMessageID'
|
||||
&& $this->datacenter !== $args['id']['dc_id']
|
||||
) {
|
||||
if (\is_array($args) && isset($args['id']['_']) && isset($args['id']['dc_id']) && $args['id']['_'] === 'inputBotInlineMessageID' && $this->datacenter !== $args['id']['dc_id']) {
|
||||
$aargs['datacenter'] = $args['id']['dc_id'];
|
||||
return $this->API->methodCallAsyncWriteGenerator($method, $args, $aargs);
|
||||
}
|
||||
@ -155,7 +138,6 @@ trait CallHandler
|
||||
if (\in_array($method, ['messages.setEncryptedTyping', 'messages.readEncryptedHistory', 'messages.sendEncrypted', 'messages.sendEncryptedFile', 'messages.sendEncryptedService', 'messages.receivedQueue'])) {
|
||||
$aargs['queue'] = 'secret';
|
||||
}
|
||||
|
||||
if (\is_array($args)) {
|
||||
if (isset($args['multiple'])) {
|
||||
$aargs['multiple'] = true;
|
||||
@ -170,18 +152,15 @@ trait CallHandler
|
||||
$new_aargs = $aargs;
|
||||
$new_aargs['postpone'] = true;
|
||||
unset($new_aargs['multiple']);
|
||||
|
||||
if (isset($args['multiple'])) {
|
||||
unset($args['multiple']);
|
||||
}
|
||||
foreach ($args as $single_args) {
|
||||
$promises[] = $this->methodCallAsyncWrite($method, $single_args, $new_aargs);
|
||||
}
|
||||
|
||||
if (!isset($aargs['postpone'])) {
|
||||
$this->writer->resume();
|
||||
}
|
||||
|
||||
return yield all($promises);
|
||||
}
|
||||
$args = yield $this->API->botAPIToMTProto($args);
|
||||
@ -189,37 +168,21 @@ trait CallHandler
|
||||
$args['ping_id'] = Tools::packSignedLong($args['ping_id']);
|
||||
}
|
||||
}
|
||||
|
||||
$deferred = new Deferred();
|
||||
$message = \array_merge(
|
||||
$aargs,
|
||||
[
|
||||
'_' => $method,
|
||||
'type' => $this->API->getTL()->getMethods()->findByMethod($method)['type'],
|
||||
'contentRelated' => $this->contentRelated($method),
|
||||
'promise' => $deferred,
|
||||
'method' => true,
|
||||
'unencrypted' => !$this->shared->hasTempAuthKey() && \strpos($method, '.') === false
|
||||
]
|
||||
);
|
||||
|
||||
$message = \array_merge($aargs, ['_' => $method, 'type' => $this->API->getTL()->getMethods()->findByMethod($method)['type'], 'contentRelated' => $this->contentRelated($method), 'promise' => $deferred, 'method' => true, 'unencrypted' => !$this->shared->hasTempAuthKey() && \strpos($method, '.') === false]);
|
||||
if (\is_object($args) && $args instanceof AsyncParameters) {
|
||||
$message['body'] = yield $args->fetchParameters();
|
||||
} else {
|
||||
$message['body'] = $args;
|
||||
}
|
||||
|
||||
if (($method === 'users.getUsers' && $args === ['id' => [['_' => 'inputUserSelf']]]) || $method === 'auth.exportAuthorization' || $method === 'updates.getDifference') {
|
||||
if ($method === 'users.getUsers' && $args === ['id' => [['_' => 'inputUserSelf']]] || $method === 'auth.exportAuthorization' || $method === 'updates.getDifference') {
|
||||
$message['user_related'] = true;
|
||||
}
|
||||
$aargs['postpone'] = $aargs['postpone'] ?? false;
|
||||
$deferred = yield $this->sendMessage($message, !$aargs['postpone']);
|
||||
|
||||
$this->checker->resume();
|
||||
|
||||
return $deferred;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send object and make sure it is asynchronously sent (generator).
|
||||
*
|
||||
@ -235,7 +198,6 @@ trait CallHandler
|
||||
if (isset($aargs['promise'])) {
|
||||
$message['promise'] = $aargs['promise'];
|
||||
}
|
||||
|
||||
$aargs['postpone'] = $aargs['postpone'] ?? false;
|
||||
return $this->sendMessage($message, !$aargs['postpone']);
|
||||
}
|
||||
|
@ -26,7 +26,6 @@ trait MsgIdHandler
|
||||
{
|
||||
public $max_incoming_id;
|
||||
public $max_outgoing_id;
|
||||
|
||||
public function checkMessageId($new_message_id, $aargs)
|
||||
{
|
||||
if (!\is_object($new_message_id)) {
|
||||
@ -81,7 +80,6 @@ trait MsgIdHandler
|
||||
$this->incoming_messages[\strrev($new_message_id->toBytes())] = [];
|
||||
}
|
||||
}
|
||||
|
||||
public function generateMessageId()
|
||||
{
|
||||
$message_id = (new \tgseclib\Math\BigInteger(\time() + $this->time_delta))->bitwise_leftShift(32);
|
||||
@ -89,17 +87,14 @@ trait MsgIdHandler
|
||||
$message_id = $key->add(\danog\MadelineProto\Magic::$four);
|
||||
}
|
||||
$this->checkMessageId($message_id, ['outgoing' => true, 'container' => false]);
|
||||
|
||||
return \strrev($message_id->toBytes());
|
||||
}
|
||||
|
||||
public function getMaxId($incoming)
|
||||
{
|
||||
$incoming = $incoming ? 'incoming' : 'outgoing';
|
||||
if (isset($this->{'max_' . $incoming . '_id'}) && \is_object($this->{'max_' . $incoming . '_id'})) {
|
||||
return $this->{'max_' . $incoming . '_id'};
|
||||
}
|
||||
|
||||
return \danog\MadelineProto\Magic::$zero;
|
||||
}
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ use danog\MadelineProto\MTProto;
|
||||
*/
|
||||
trait ResponseHandler
|
||||
{
|
||||
public function sendMsgsStateInfo($req_msg_id, $msg_ids)
|
||||
public function sendMsgsStateInfo($req_msg_id, $msg_ids): \Generator
|
||||
{
|
||||
$this->logger->logger('Sending state info for ' . \count($msg_ids) . ' message IDs');
|
||||
$info = '';
|
||||
@ -37,26 +37,24 @@ trait ResponseHandler
|
||||
if (!isset($this->incoming_messages[$msg_id])) {
|
||||
$msg_id = new \tgseclib\Math\BigInteger(\strrev($msg_id), 256);
|
||||
if ((new \tgseclib\Math\BigInteger(\time() + $this->time_delta + 30))->bitwise_leftShift(32)->compare($msg_id) < 0) {
|
||||
$this->logger->logger("Do not know anything about $msg_id and it is too small");
|
||||
$this->logger->logger("Do not know anything about {$msg_id} and it is too small");
|
||||
$cur_info |= 3;
|
||||
} elseif ((new \tgseclib\Math\BigInteger(\time() + $this->time_delta - 300))->bitwise_leftShift(32)->compare($msg_id) > 0) {
|
||||
$this->logger->logger("Do not know anything about $msg_id and it is too big");
|
||||
$this->logger->logger("Do not know anything about {$msg_id} and it is too big");
|
||||
$cur_info |= 1;
|
||||
} else {
|
||||
$this->logger->logger("Do not know anything about $msg_id");
|
||||
$this->logger->logger("Do not know anything about {$msg_id}");
|
||||
$cur_info |= 2;
|
||||
}
|
||||
} else {
|
||||
$this->logger->logger("Know about $msg_id");
|
||||
$this->logger->logger("Know about {$msg_id}");
|
||||
$cur_info |= 4;
|
||||
}
|
||||
$info .= \chr($cur_info);
|
||||
}
|
||||
$this->outgoing_messages[yield $this->objectCall('msgs_state_info', ['req_msg_id' => $req_msg_id, 'info' => $info], ['postpone' => true])]['response'] = $req_msg_id;
|
||||
}
|
||||
|
||||
public $n = 0;
|
||||
|
||||
public function handleMessages()
|
||||
{
|
||||
$only_updates = true;
|
||||
@ -68,7 +66,6 @@ trait ResponseHandler
|
||||
continue;
|
||||
}
|
||||
$this->logger->logger((isset($this->incoming_messages[$current_msg_id]['from_container']) ? 'Inside of container, received ' : 'Received ') . $this->incoming_messages[$current_msg_id]['content']['_'] . ' from DC ' . $this->datacenter, \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
|
||||
switch ($this->incoming_messages[$current_msg_id]['content']['_']) {
|
||||
case 'msgs_ack':
|
||||
unset($this->new_incoming[$current_msg_id]);
|
||||
@ -78,7 +75,6 @@ trait ResponseHandler
|
||||
$this->ackOutgoingMessageId($msg_id);
|
||||
// Acknowledge that the server received my message
|
||||
}
|
||||
|
||||
unset($this->incoming_messages[$current_msg_id]['content']);
|
||||
break;
|
||||
case 'rpc_result':
|
||||
@ -89,10 +85,8 @@ trait ResponseHandler
|
||||
$req_msg_id = $this->incoming_messages[$current_msg_id]['content']['req_msg_id'];
|
||||
$this->incoming_messages[$current_msg_id]['content'] = $this->incoming_messages[$current_msg_id]['content']['result'];
|
||||
$this->checkInSeqNo($current_msg_id);
|
||||
|
||||
$this->handleResponse($req_msg_id, $current_msg_id);
|
||||
break;
|
||||
|
||||
case 'future_salts':
|
||||
case 'msgs_state_info':
|
||||
$msg_id_type = 'req_msg_id';
|
||||
@ -106,30 +100,24 @@ trait ResponseHandler
|
||||
unset($this->new_incoming[$current_msg_id]);
|
||||
$this->checkInSeqNo($current_msg_id);
|
||||
$only_updates = false;
|
||||
|
||||
$this->handleResponse($this->incoming_messages[$current_msg_id]['content'][$msg_id_type], $current_msg_id);
|
||||
unset($msg_id_type);
|
||||
break;
|
||||
|
||||
case 'new_session_created':
|
||||
unset($this->new_incoming[$current_msg_id]);
|
||||
$this->checkInSeqNo($current_msg_id);
|
||||
$only_updates = false;
|
||||
|
||||
$this->shared->getTempAuthKey()->setServerSalt($this->incoming_messages[$current_msg_id]['content']['server_salt']);
|
||||
$this->ackIncomingMessageId($current_msg_id);
|
||||
|
||||
// Acknowledge that I received the server's response
|
||||
if ($this->API->authorized === MTProto::LOGGED_IN && !$this->API->isInitingAuthorization() && $this->API->datacenter->getDataCenterConnection($this->API->datacenter->curdc)->hasTempAuthKey() && isset($this->API->updaters[false])) {
|
||||
$this->API->updaters[false]->resumeDefer();
|
||||
}
|
||||
|
||||
unset($this->incoming_messages[$current_msg_id]['content']);
|
||||
break;
|
||||
case 'msg_container':
|
||||
unset($this->new_incoming[$current_msg_id]);
|
||||
$only_updates = false;
|
||||
|
||||
foreach ($this->incoming_messages[$current_msg_id]['content']['messages'] as $message) {
|
||||
$this->checkMessageId($message['msg_id'], ['outgoing' => false, 'container' => true]);
|
||||
$this->incoming_messages[$message['msg_id']] = ['seq_no' => $message['seqno'], 'content' => $message['body'], 'from_container' => true];
|
||||
@ -138,14 +126,12 @@ trait ResponseHandler
|
||||
\ksort($this->new_incoming);
|
||||
//$this->handleMessages();
|
||||
//$this->checkInSeqNo($current_msg_id);
|
||||
|
||||
unset($this->incoming_messages[$current_msg_id]['content']);
|
||||
break;
|
||||
case 'msg_copy':
|
||||
unset($this->new_incoming[$current_msg_id]);
|
||||
$this->checkInSeqNo($current_msg_id);
|
||||
$only_updates = false;
|
||||
|
||||
$this->ackIncomingMessageId($current_msg_id);
|
||||
// Acknowledge that I received the server's response
|
||||
if (isset($this->incoming_messages[$this->incoming_messages[$current_msg_id]['content']['orig_message']['msg_id']])) {
|
||||
@ -157,25 +143,19 @@ trait ResponseHandler
|
||||
$this->incoming_messages[$message['orig_message']['msg_id']] = ['content' => $this->incoming_messages[$current_msg_id]['content']['orig_message']];
|
||||
$this->new_incoming[$message['orig_message']['msg_id']] = $message['orig_message']['msg_id'];
|
||||
}
|
||||
|
||||
unset($this->incoming_messages[$current_msg_id]['content']);
|
||||
break;
|
||||
|
||||
case 'http_wait':
|
||||
unset($this->new_incoming[$current_msg_id]);
|
||||
$this->checkInSeqNo($current_msg_id);
|
||||
$only_updates = false;
|
||||
|
||||
$this->logger->logger($this->incoming_messages[$current_msg_id]['content'], \danog\MadelineProto\Logger::NOTICE);
|
||||
|
||||
unset($this->incoming_messages[$current_msg_id]['content']);
|
||||
break;
|
||||
|
||||
case 'msgs_state_req':
|
||||
$this->checkInSeqNo($current_msg_id);
|
||||
$only_updates = false;
|
||||
unset($this->new_incoming[$current_msg_id]);
|
||||
|
||||
\danog\MadelineProto\Tools::callFork($this->sendMsgsStateInfo($current_msg_id, $this->incoming_messages[$current_msg_id]['content']['msg_ids']));
|
||||
unset($this->incoming_messages[$current_msg_id]['content']);
|
||||
break;
|
||||
@ -183,7 +163,6 @@ trait ResponseHandler
|
||||
$this->checkInSeqNo($current_msg_id);
|
||||
$only_updates = false;
|
||||
unset($this->new_incoming[$current_msg_id]);
|
||||
|
||||
foreach ($this->incoming_messages[$current_msg_id]['content']['msg_ids'] as $key => $msg_id) {
|
||||
$info = \ord($this->incoming_messages[$current_msg_id]['content']['info'][$key]);
|
||||
$msg_id = new \tgseclib\Math\BigInteger(\strrev($msg_id), 256);
|
||||
@ -203,7 +182,6 @@ trait ResponseHandler
|
||||
case 'msg_detailed_info':
|
||||
$this->checkInSeqNo($current_msg_id);
|
||||
unset($this->new_incoming[$current_msg_id]);
|
||||
|
||||
$only_updates = false;
|
||||
if (isset($this->outgoing_messages[$this->incoming_messages[$current_msg_id]['content']['msg_id']])) {
|
||||
if (isset($this->incoming_messages[$this->incoming_messages[$current_msg_id]['content']['answer_msg_id']])) {
|
||||
@ -217,7 +195,6 @@ trait ResponseHandler
|
||||
$this->checkInSeqNo($current_msg_id);
|
||||
$only_updates = false;
|
||||
unset($this->new_incoming[$current_msg_id]);
|
||||
|
||||
if (isset($this->incoming_messages[$this->incoming_messages[$current_msg_id]['content']['answer_msg_id']])) {
|
||||
$this->ackIncomingMessageId($this->incoming_messages[$current_msg_id]['content']['answer_msg_id']);
|
||||
} else {
|
||||
@ -228,7 +205,6 @@ trait ResponseHandler
|
||||
$this->checkInSeqNo($current_msg_id);
|
||||
$only_updates = false;
|
||||
unset($this->new_incoming[$current_msg_id]);
|
||||
|
||||
$ok = true;
|
||||
foreach ($this->incoming_messages[$current_msg_id]['content']['msg_ids'] as $msg_id) {
|
||||
if (!isset($this->outgoing_messages[$msg_id]) || isset($this->incoming_messages[$msg_id])) {
|
||||
@ -247,7 +223,6 @@ trait ResponseHandler
|
||||
$this->checkInSeqNo($current_msg_id);
|
||||
$only_updates = false;
|
||||
unset($this->new_incoming[$current_msg_id]);
|
||||
|
||||
\danog\MadelineProto\Tools::callFork($this->sendMsgsStateInfo($current_msg_id, $this->incoming_messages[$current_msg_id]['content']['msg_ids']));
|
||||
foreach ($this->incoming_messages[$current_msg_id]['content']['msg_ids'] as $msg_id) {
|
||||
if (isset($this->incoming_messages[$msg_id]['response']) && isset($this->outgoing_messages[$this->incoming_messages[$msg_id]['response']])) {
|
||||
@ -260,17 +235,13 @@ trait ResponseHandler
|
||||
$this->ackIncomingMessageId($current_msg_id);
|
||||
// Acknowledge that I received the server's response
|
||||
$response_type = $this->API->getTL()->getConstructors()->findByPredicate($this->incoming_messages[$current_msg_id]['content']['_'])['type'];
|
||||
|
||||
switch ($response_type) {
|
||||
case 'Updates':
|
||||
unset($this->new_incoming[$current_msg_id]);
|
||||
|
||||
if (!$this->isCdn()) {
|
||||
\danog\MadelineProto\Tools::callForkDefer($this->API->handleUpdates($this->incoming_messages[$current_msg_id]['content']));
|
||||
}
|
||||
|
||||
unset($this->incoming_messages[$current_msg_id]['content']);
|
||||
|
||||
$only_updates = true && $only_updates;
|
||||
break;
|
||||
default:
|
||||
@ -281,7 +252,6 @@ trait ResponseHandler
|
||||
if (!isset($expecting['type'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->logger->logger('Does the request of return type ' . $expecting['type'] . ' match?', \danog\MadelineProto\Logger::VERBOSE);
|
||||
if ($response_type === $expecting['type']) {
|
||||
$this->logger->logger('Yes', \danog\MadelineProto\Logger::VERBOSE);
|
||||
@ -291,7 +261,6 @@ trait ResponseHandler
|
||||
}
|
||||
$this->logger->logger('No', \danog\MadelineProto\Logger::VERBOSE);
|
||||
}
|
||||
|
||||
$this->logger->logger('Dunno how to handle ' . PHP_EOL . \var_export($this->incoming_messages[$current_msg_id]['content'], true), \danog\MadelineProto\Logger::FATAL_ERROR);
|
||||
unset($this->new_incoming[$current_msg_id]);
|
||||
break;
|
||||
@ -302,12 +271,9 @@ trait ResponseHandler
|
||||
if ($this->pending_outgoing) {
|
||||
$this->writer->resume();
|
||||
}
|
||||
|
||||
//$this->n--;
|
||||
|
||||
return $only_updates;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reject request with exception.
|
||||
*
|
||||
@ -322,8 +288,7 @@ trait ResponseHandler
|
||||
Loop::defer(function () use (&$request, $data) {
|
||||
if (isset($request['promise'])) {
|
||||
$this->logger->logger('Rejecting: ' . (isset($request['_']) ? $request['_'] : '-'));
|
||||
$this->logger->logger("Rejecting: $data");
|
||||
|
||||
$this->logger->logger("Rejecting: {$data}");
|
||||
$promise = $request['promise'];
|
||||
unset($request['promise']);
|
||||
try {
|
||||
@ -336,7 +301,7 @@ trait ResponseHandler
|
||||
}
|
||||
} else {
|
||||
$this->logger->logger('Rejecting: already got response for ' . (isset($request['_']) ? $request['_'] : '-'));
|
||||
$this->logger->logger("Rejecting: $data");
|
||||
$this->logger->logger("Rejecting: {$data}");
|
||||
}
|
||||
});
|
||||
} elseif (isset($request['container'])) {
|
||||
@ -345,27 +310,23 @@ trait ResponseHandler
|
||||
}
|
||||
} else {
|
||||
$this->logger->logger('Rejecting: already got response for ' . (isset($request['_']) ? $request['_'] : '-'));
|
||||
$this->logger->logger("Rejecting: $data");
|
||||
$this->logger->logger("Rejecting: {$data}");
|
||||
}
|
||||
}
|
||||
|
||||
public function handleResponse($request_id, $response_id)
|
||||
{
|
||||
$response =& $this->incoming_messages[$response_id]['content'];
|
||||
unset($this->incoming_messages[$response_id]['content']);
|
||||
$request =& $this->outgoing_messages[$request_id];
|
||||
|
||||
if (isset($response['_'])) {
|
||||
switch ($response['_']) {
|
||||
case 'rpc_error':
|
||||
if (($request['method'] ?? false) && $request['_'] !== 'auth.bindTempAuthKey' && $this->shared->hasTempAuthKey() && !$this->shared->getTempAuthKey()->isInited()) {
|
||||
$this->shared->getTempAuthKey()->init(true);
|
||||
}
|
||||
|
||||
if (\in_array($response['error_message'], ['PERSISTENT_TIMESTAMP_EMPTY', 'PERSISTENT_TIMESTAMP_INVALID'])) {
|
||||
$this->gotResponseForOutgoingMessageId($request_id);
|
||||
$this->handleReject($request, new \danog\MadelineProto\PTSException($response['error_message']));
|
||||
|
||||
return;
|
||||
}
|
||||
if ($response['error_message'] === 'PERSISTENT_TIMESTAMP_OUTDATED') {
|
||||
@ -373,14 +334,11 @@ trait ResponseHandler
|
||||
}
|
||||
if (\strpos($response['error_message'], 'FILE_REFERENCE_') === 0) {
|
||||
$this->logger->logger("Got {$response['error_message']}, refreshing file reference and repeating method call...");
|
||||
|
||||
$request['refreshReferences'] = true;
|
||||
if (isset($request['serialized_body'])) {
|
||||
unset($request['serialized_body']);
|
||||
}
|
||||
|
||||
$this->methodRecall('', ['message_id' => $request_id, 'postpone' => true]);
|
||||
|
||||
return;
|
||||
}
|
||||
switch ($response['error_code']) {
|
||||
@ -389,32 +347,26 @@ trait ResponseHandler
|
||||
if ($response['error_message'] === 'MSG_WAIT_FAILED') {
|
||||
$this->call_queue[$request['queue']] = [];
|
||||
$this->methodRecall('', ['message_id' => $request_id, 'postpone' => true]);
|
||||
|
||||
return;
|
||||
}
|
||||
if (\in_array($response['error_message'], ['MSGID_DECREASE_RETRY', 'HISTORY_GET_FAILED', 'RPC_CALL_FAIL', 'PERSISTENT_TIMESTAMP_OUTDATED', 'RPC_MCGET_FAIL', 'no workers running', 'No workers running'])) {
|
||||
Loop::delay(1 * 1000, [$this, 'methodRecall'], ['message_id' => $request_id, ]);
|
||||
Loop::delay(1 * 1000, [$this, 'methodRecall'], ['message_id' => $request_id]);
|
||||
return;
|
||||
}
|
||||
$this->gotResponseForOutgoingMessageId($request_id);
|
||||
|
||||
$this->handleReject($request, new \danog\MadelineProto\RPCErrorException($response['error_message'], $response['error_code'], $request['_'] ?? ''));
|
||||
|
||||
return;
|
||||
case 303:
|
||||
$this->API->datacenter->curdc = $datacenter = (int) \preg_replace('/[^0-9]+/', '', $response['error_message']);
|
||||
|
||||
if (isset($request['file']) && $request['file'] && $this->API->datacenter->has($datacenter . '_media')) {
|
||||
\danog\MadelineProto\Logger::log('Using media DC');
|
||||
$datacenter .= '_media';
|
||||
}
|
||||
|
||||
if (isset($request['user_related']) && $request['user_related']) {
|
||||
$this->API->settings['connection_settings']['default_dc'] = $this->API->authorized_dc = $this->API->datacenter->curdc;
|
||||
}
|
||||
Loop::defer([$this, 'methodRecall'], ['message_id' => $request_id, 'datacenter' => $datacenter]);
|
||||
//$this->API->methodRecall('', ['message_id' => $request_id, 'datacenter' => $datacenter, 'postpone' => true]);
|
||||
|
||||
return;
|
||||
case 401:
|
||||
switch ($response['error_message']) {
|
||||
@ -422,14 +374,12 @@ trait ResponseHandler
|
||||
case 'SESSION_REVOKED':
|
||||
case 'SESSION_EXPIRED':
|
||||
$this->gotResponseForOutgoingMessageId($request_id);
|
||||
|
||||
$this->logger->logger($response['error_message'], \danog\MadelineProto\Logger::FATAL_ERROR);
|
||||
foreach ($this->API->datacenter->getDataCenterConnections() as $socket) {
|
||||
$socket->setTempAuthKey(null);
|
||||
$socket->setPermAuthKey(null);
|
||||
$socket->resetSession();
|
||||
}
|
||||
|
||||
if ($response['error_message'] === 'USER_DEACTIVATED') {
|
||||
$this->logger->logger('!!!!!!! WARNING !!!!!!!', \danog\MadelineProto\Logger::FATAL_ERROR);
|
||||
$this->logger->logger("Telegram's flood prevention system suspended this account.", \danog\MadelineProto\Logger::ERROR);
|
||||
@ -439,44 +389,33 @@ trait ResponseHandler
|
||||
$this->logger->logger('Then login again.', \danog\MadelineProto\Logger::FATAL_ERROR);
|
||||
$this->logger->logger('If you intentionally deleted this account, ignore this message.', \danog\MadelineProto\Logger::FATAL_ERROR);
|
||||
}
|
||||
|
||||
$this->API->resetSession();
|
||||
|
||||
\danog\MadelineProto\Tools::callFork((function () use (&$request, &$response) {
|
||||
\danog\MadelineProto\Tools::callFork((function () use (&$request, &$response): \Generator {
|
||||
yield $this->API->initAuthorization();
|
||||
|
||||
$this->handleReject($request, new \danog\MadelineProto\RPCErrorException($response['error_message'], $response['error_code'], $request['_'] ?? ''));
|
||||
})());
|
||||
|
||||
return;
|
||||
case 'AUTH_KEY_UNREGISTERED':
|
||||
case 'AUTH_KEY_INVALID':
|
||||
if ($this->API->authorized !== MTProto::LOGGED_IN) {
|
||||
$this->gotResponseForOutgoingMessageId($request_id);
|
||||
|
||||
\danog\MadelineProto\Tools::callFork((function () use (&$request, &$response) {
|
||||
\danog\MadelineProto\Tools::callFork((function () use (&$request, &$response): \Generator {
|
||||
yield $this->API->initAuthorization();
|
||||
|
||||
$this->handleReject($request, new \danog\MadelineProto\RPCErrorException($response['error_message'], $response['error_code'], $request['_'] ?? ''));
|
||||
})());
|
||||
|
||||
return;
|
||||
}
|
||||
$this->session_id = null;
|
||||
$this->shared->setTempAuthKey(null);
|
||||
$this->shared->setPermAuthKey(null);
|
||||
|
||||
$this->logger->logger('Auth key not registered, resetting temporary and permanent auth keys...', \danog\MadelineProto\Logger::ERROR);
|
||||
|
||||
if ($this->API->authorized_dc === $this->datacenter && $this->API->authorized === MTProto::LOGGED_IN) {
|
||||
$this->gotResponseForOutgoingMessageId($request_id);
|
||||
|
||||
$this->logger->logger('Permanent auth key was main authorized key, logging out...', \danog\MadelineProto\Logger::FATAL_ERROR);
|
||||
foreach ($this->API->datacenter->getDataCenterConnections() as $socket) {
|
||||
$socket->setTempAuthKey(null);
|
||||
$socket->setPermAuthKey(null);
|
||||
}
|
||||
|
||||
$this->logger->logger('!!!!!!! WARNING !!!!!!!', \danog\MadelineProto\Logger::FATAL_ERROR);
|
||||
$this->logger->logger("Telegram's flood prevention system suspended this account.", \danog\MadelineProto\Logger::ERROR);
|
||||
$this->logger->logger('To continue, manual verification is required.', \danog\MadelineProto\Logger::FATAL_ERROR);
|
||||
@ -484,61 +423,46 @@ trait ResponseHandler
|
||||
$this->logger->logger('Send an email to recover@telegram.org, asking to unban the phone number ' . $phone . ', and quickly describe what will you do with this phone number.', \danog\MadelineProto\Logger::FATAL_ERROR);
|
||||
$this->logger->logger('Then login again.', \danog\MadelineProto\Logger::FATAL_ERROR);
|
||||
$this->logger->logger('If you intentionally deleted this account, ignore this message.', \danog\MadelineProto\Logger::FATAL_ERROR);
|
||||
|
||||
$this->API->resetSession();
|
||||
|
||||
\danog\MadelineProto\Tools::callFork((function () use (&$request, &$response) {
|
||||
\danog\MadelineProto\Tools::callFork((function () use (&$request, &$response): \Generator {
|
||||
yield $this->API->initAuthorization();
|
||||
|
||||
$this->handleReject($request, new \danog\MadelineProto\RPCErrorException($response['error_message'], $response['error_code'], $request['_'] ?? ''));
|
||||
})());
|
||||
|
||||
return;
|
||||
}
|
||||
\danog\MadelineProto\Tools::callFork((function () use ($request_id) {
|
||||
\danog\MadelineProto\Tools::callFork((function () use ($request_id): \Generator {
|
||||
yield $this->API->initAuthorization();
|
||||
|
||||
$this->methodRecall('', ['message_id' => $request_id, ]);
|
||||
$this->methodRecall('', ['message_id' => $request_id]);
|
||||
})());
|
||||
|
||||
return;
|
||||
case 'AUTH_KEY_PERM_EMPTY':
|
||||
$this->logger->logger('Temporary auth key not bound, resetting temporary auth key...', \danog\MadelineProto\Logger::ERROR);
|
||||
|
||||
$this->shared->setTempAuthKey(null);
|
||||
\danog\MadelineProto\Tools::callFork((function () use ($request_id) {
|
||||
\danog\MadelineProto\Tools::callFork((function () use ($request_id): \Generator {
|
||||
yield $this->API->initAuthorization();
|
||||
$this->methodRecall('', ['message_id' => $request_id, ]);
|
||||
$this->methodRecall('', ['message_id' => $request_id]);
|
||||
})());
|
||||
|
||||
return;
|
||||
}
|
||||
$this->gotResponseForOutgoingMessageId($request_id);
|
||||
|
||||
$this->handleReject($request, new \danog\MadelineProto\RPCErrorException($response['error_message'], $response['error_code'], $request['_'] ?? ''));
|
||||
|
||||
return;
|
||||
case 420:
|
||||
$seconds = \preg_replace('/[^0-9]+/', '', $response['error_message']);
|
||||
$limit = $request['FloodWaitLimit'] ?? $this->API->settings['flood_timeout']['wait_if_lt'];
|
||||
if (\is_numeric($seconds) && $seconds < $limit) {
|
||||
//$this->gotResponseForOutgoingMessageId($request_id);
|
||||
|
||||
$this->logger->logger('Flood, waiting ' . $seconds . ' seconds before repeating async call of ' . ($request['_'] ?? '') . '...', \danog\MadelineProto\Logger::NOTICE);
|
||||
$request['sent'] = ($request['sent'] ?? \time()) + $seconds;
|
||||
Loop::delay($seconds * 1000, [$this, 'methodRecall'], ['message_id' => $request_id, ]);
|
||||
|
||||
Loop::delay($seconds * 1000, [$this, 'methodRecall'], ['message_id' => $request_id]);
|
||||
return;
|
||||
}
|
||||
// no break
|
||||
default:
|
||||
$this->gotResponseForOutgoingMessageId($request_id);
|
||||
|
||||
$this->handleReject($request, new \danog\MadelineProto\RPCErrorException($response['error_message'], $response['error_code'], $request['_'] ?? ''));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
return;
|
||||
case 'boolTrue':
|
||||
case 'boolFalse':
|
||||
@ -551,7 +475,6 @@ trait ResponseHandler
|
||||
case 48:
|
||||
$this->shared->getTempAuthKey()->setServerSalt($response['new_server_salt']);
|
||||
$this->methodRecall('', ['message_id' => $request_id, 'postpone' => true]);
|
||||
|
||||
return;
|
||||
case 16:
|
||||
case 17:
|
||||
@ -559,28 +482,23 @@ trait ResponseHandler
|
||||
$this->logger->logger('Set time delta to ' . $this->time_delta, \danog\MadelineProto\Logger::WARNING);
|
||||
$this->API->resetMTProtoSession();
|
||||
$this->shared->setTempAuthKey(null);
|
||||
\danog\MadelineProto\Tools::callFork((function () use ($request_id) {
|
||||
\danog\MadelineProto\Tools::callFork((function () use ($request_id): \Generator {
|
||||
yield $this->API->initAuthorization();
|
||||
$this->methodRecall('', ['message_id' => $request_id, ]);
|
||||
$this->methodRecall('', ['message_id' => $request_id]);
|
||||
})());
|
||||
|
||||
return;
|
||||
}
|
||||
$this->gotResponseForOutgoingMessageId($request_id);
|
||||
$this->handleReject($request, new \danog\MadelineProto\RPCErrorException('Received bad_msg_notification: ' . MTProto::BAD_MSG_ERROR_CODES[$response['error_code']], $response['error_code'], $request['_'] ?? ''));
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (($request['method'] ?? false) && $request['_'] !== 'auth.bindTempAuthKey' && $this->shared->hasTempAuthKey() && !$this->shared->getTempAuthKey()->isInited()) {
|
||||
$this->shared->getTempAuthKey()->init(true);
|
||||
}
|
||||
|
||||
if (!isset($request['promise'])) {
|
||||
$this->gotResponseForOutgoingMessageId($request_id);
|
||||
$this->logger->logger('Response: already got response for ' . (isset($request['_']) ? $request['_'] : '-') . ' with message ID ' . $request_id);
|
||||
|
||||
return;
|
||||
}
|
||||
$botAPI = isset($request['botAPI']) && $request['botAPI'];
|
||||
@ -591,15 +509,15 @@ trait ResponseHandler
|
||||
unset($request);
|
||||
$this->gotResponseForOutgoingMessageId($request_id);
|
||||
$r = isset($response['_']) ? $response['_'] : \json_encode($response);
|
||||
$this->logger->logger("Defer sending $r to deferred", Logger::ULTRA_VERBOSE);
|
||||
\danog\MadelineProto\Tools::callFork((
|
||||
function () use ($request_id, $response, $botAPI) {
|
||||
$this->logger->logger("Defer sending {$r} to deferred", Logger::ULTRA_VERBOSE);
|
||||
\danog\MadelineProto\Tools::callFork((function () use ($request_id, $response, $botAPI): \Generator {
|
||||
$r = isset($response['_']) ? $response['_'] : \json_encode($response);
|
||||
$this->logger->logger("Deferred: sent $r to deferred", Logger::ULTRA_VERBOSE);
|
||||
$this->logger->logger("Deferred: sent {$r} to deferred", Logger::ULTRA_VERBOSE);
|
||||
if ($botAPI) {
|
||||
$response = yield $this->MTProtoToBotAPI($response);
|
||||
}
|
||||
if (isset($this->outgoing_messages[$request_id]['promise'])) { // This should not happen but happens, should debug
|
||||
if (isset($this->outgoing_messages[$request_id]['promise'])) {
|
||||
// This should not happen but happens, should debug
|
||||
$promise = $this->outgoing_messages[$request_id]['promise'];
|
||||
unset($this->outgoing_messages[$request_id]['promise']);
|
||||
try {
|
||||
@ -611,7 +529,6 @@ trait ResponseHandler
|
||||
$this->logger->logger("Got promise already resolved error", \danog\MadelineProto\Logger::FATAL_ERROR);
|
||||
}
|
||||
}
|
||||
}
|
||||
)());
|
||||
})());
|
||||
}
|
||||
}
|
||||
|
@ -28,9 +28,7 @@ trait SeqNoHandler
|
||||
{
|
||||
public $session_out_seq_no = 0;
|
||||
public $session_in_seq_no = 0;
|
||||
|
||||
public $session_id;
|
||||
|
||||
public function generateOutSeqNo($contentRelated)
|
||||
{
|
||||
$in = $contentRelated ? 1 : 0;
|
||||
@ -39,7 +37,6 @@ trait SeqNoHandler
|
||||
//$this->API->logger->logger("OUT: $value + $in = ".$this->session_out_seq_no);
|
||||
return $value * 2 + $in;
|
||||
}
|
||||
|
||||
public function checkInSeqNo($current_msg_id)
|
||||
{
|
||||
$type = isset($this->incoming_messages[$current_msg_id]['content']['_']) ? $this->incoming_messages[$current_msg_id]['content']['_'] : '-';
|
||||
@ -49,7 +46,6 @@ trait SeqNoHandler
|
||||
$this->API->logger->logger('Seqno OK (should be ' . $seq_no . ', is ' . $this->incoming_messages[$current_msg_id]['seq_no'] . ', ' . $type . ')', \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
}
|
||||
}
|
||||
|
||||
public function generateInSeqNo($contentRelated)
|
||||
{
|
||||
$in = $contentRelated ? 1 : 0;
|
||||
@ -58,11 +54,9 @@ trait SeqNoHandler
|
||||
//$this->API->logger->logger("IN: $value + $in = ".$this->session_in_seq_no);
|
||||
return $value * 2 + $in;
|
||||
}
|
||||
|
||||
public function contentRelated($method)
|
||||
{
|
||||
$method = \is_array($method) && isset($method['_']) ? $method['_'] : $method;
|
||||
|
||||
return \is_string($method) ? !\in_array($method, MTProto::NOT_CONTENT_RELATED) : true;
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Session module.
|
||||
*
|
||||
@ -28,20 +29,15 @@ abstract class Session
|
||||
use ResponseHandler;
|
||||
use SeqNoHandler;
|
||||
use CallHandler;
|
||||
|
||||
public $incoming_messages = [];
|
||||
public $outgoing_messages = [];
|
||||
public $new_incoming = [];
|
||||
public $new_outgoing = [];
|
||||
|
||||
public $pending_outgoing = [];
|
||||
public $pending_outgoing_key = 'a';
|
||||
|
||||
public $time_delta = 0;
|
||||
|
||||
public $call_queue = [];
|
||||
public $ack_queue = [];
|
||||
|
||||
/**
|
||||
* Reset MTProto session.
|
||||
*
|
||||
@ -68,7 +64,6 @@ abstract class Session
|
||||
$this->session_out_seq_no = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Backup eventual unsent messages before session deletion.
|
||||
*
|
||||
|
@ -46,7 +46,6 @@ trait AuthKeyHandler
|
||||
* @var boolean
|
||||
*/
|
||||
private $pending_auth = false;
|
||||
|
||||
/**
|
||||
* Create authorization key.
|
||||
*
|
||||
@ -62,7 +61,6 @@ trait AuthKeyHandler
|
||||
$connection = $this->datacenter->getAuthConnection($datacenter);
|
||||
$cdn = $connection->isCDN();
|
||||
$req_pq = $cdn ? 'req_pq' : 'req_pq_multi';
|
||||
|
||||
for ($retry_id_total = 1; $retry_id_total <= $this->settings['max_tries']['authorization']; $retry_id_total++) {
|
||||
try {
|
||||
$this->logger->logger(\danog\MadelineProto\Lang::$current_lang['req_pq'], \danog\MadelineProto\Logger::VERBOSE);
|
||||
@ -128,7 +126,6 @@ trait AuthKeyHandler
|
||||
list($p, $q) = [$q, $p];
|
||||
}
|
||||
}
|
||||
|
||||
if (!$pq->equals($p->multiply($q))) {
|
||||
$this->logger->logger('Automatic factorization failed, trying alt py module', \danog\MadelineProto\Logger::ERROR);
|
||||
$p = new \tgseclib\Math\BigInteger(\danog\PrimeModule::python_single_alt($pq->__toString()));
|
||||
@ -138,7 +135,6 @@ trait AuthKeyHandler
|
||||
list($p, $q) = [$q, $p];
|
||||
}
|
||||
}
|
||||
|
||||
if (!$pq->equals($p->multiply($q))) {
|
||||
$this->logger->logger('Automatic factorization failed, trying py module', \danog\MadelineProto\Logger::ERROR);
|
||||
$p = new \tgseclib\Math\BigInteger(\danog\PrimeModule::python_single($pq->__toString()));
|
||||
@ -148,7 +144,6 @@ trait AuthKeyHandler
|
||||
list($p, $q) = [$q, $p];
|
||||
}
|
||||
}
|
||||
|
||||
if (!$pq->equals($p->multiply($q))) {
|
||||
$this->logger->logger('Automatic factorization failed, trying native module', \danog\MadelineProto\Logger::ERROR);
|
||||
$p = new \tgseclib\Math\BigInteger(\danog\PrimeModule::native_single($pq->__toString()));
|
||||
@ -158,18 +153,15 @@ trait AuthKeyHandler
|
||||
list($p, $q) = [$q, $p];
|
||||
}
|
||||
}
|
||||
|
||||
if (!$pq->equals($p->multiply($q))) {
|
||||
$this->logger->logger('Automatic factorization failed, trying wolfram module', \danog\MadelineProto\Logger::ERROR);
|
||||
|
||||
$p = new \tgseclib\Math\BigInteger(yield $this->wolframSingle($pq->__toString()));
|
||||
$p = new \tgseclib\Math\BigInteger(yield from $this->wolframSingle($pq->__toString()));
|
||||
if (!$p->equals(\danog\MadelineProto\Magic::$zero)) {
|
||||
$q = $pq->divide($p)[0];
|
||||
if ($p->compare($q) > 0) {
|
||||
list($p, $q) = [$q, $p];
|
||||
}
|
||||
}
|
||||
|
||||
if (!$pq->equals($p->multiply($q))) {
|
||||
throw new \danog\MadelineProto\SecurityException("Couldn't compute p and q, install prime.madelineproto.xyz to fix. Original pq: {$pq}, computed p: {$p}, computed q: {$q}, computed pq: " . $p->multiply($q));
|
||||
}
|
||||
@ -178,7 +170,6 @@ trait AuthKeyHandler
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->logger->logger('Factorization ' . $pq . ' = ' . $p . ' * ' . $q, \danog\MadelineProto\Logger::VERBOSE);
|
||||
/*
|
||||
* ***********************************************************************
|
||||
@ -186,7 +177,6 @@ trait AuthKeyHandler
|
||||
*/
|
||||
$p_bytes = $p->toBytes();
|
||||
$q_bytes = $q->toBytes();
|
||||
|
||||
$new_nonce = \danog\MadelineProto\Tools::random(32);
|
||||
$data_unserialized = ['_' => 'p_q_inner_data' . ($expires_in < 0 ? '' : '_temp'), 'pq' => $pq_bytes, 'p' => $p_bytes, 'q' => $q_bytes, 'nonce' => $nonce, 'server_nonce' => $server_nonce, 'new_nonce' => $new_nonce, 'expires_in' => $expires_in, 'dc' => \preg_replace('|_.*|', '', $datacenter)];
|
||||
$p_q_inner_data = yield $this->TL->serializeObject(['type' => ''], $data_unserialized, 'p_q_inner_data');
|
||||
@ -197,7 +187,6 @@ trait AuthKeyHandler
|
||||
$sha_digest = \sha1($p_q_inner_data, true);
|
||||
$random_bytes = \danog\MadelineProto\Tools::random(255 - \strlen($p_q_inner_data) - \strlen($sha_digest));
|
||||
$to_encrypt = $sha_digest . $p_q_inner_data . $random_bytes;
|
||||
|
||||
$encrypted_data = $key->encrypt($to_encrypt);
|
||||
$this->logger->logger('Starting Diffie Hellman key exchange', \danog\MadelineProto\Logger::VERBOSE);
|
||||
/*
|
||||
@ -248,10 +237,8 @@ trait AuthKeyHandler
|
||||
* Get key, iv and decrypt answer
|
||||
*/
|
||||
$encrypted_answer = $server_dh_params['encrypted_answer'];
|
||||
|
||||
$tmp_aes_key = \sha1($new_nonce . $server_nonce, true) . \substr(\sha1($server_nonce . $new_nonce, true), 0, 12);
|
||||
$tmp_aes_iv = \substr(\sha1($server_nonce . $new_nonce, true), 12, 8) . \sha1($new_nonce . $new_nonce, true) . \substr($new_nonce, 0, 4);
|
||||
|
||||
$answer_with_hash = $this->igeDecrypt($encrypted_answer, $tmp_aes_key, $tmp_aes_iv);
|
||||
/*
|
||||
* ***********************************************************************
|
||||
@ -389,16 +376,13 @@ trait AuthKeyHandler
|
||||
throw new \danog\MadelineProto\SecurityException('wrong new_nonce_hash1');
|
||||
}
|
||||
$this->logger->logger('Diffie Hellman key exchange processed successfully!', \danog\MadelineProto\Logger::VERBOSE);
|
||||
|
||||
$key = $expires_in < 0 ? new PermAuthKey() : new TempAuthKey();
|
||||
if ($expires_in >= 0) {
|
||||
$key->expires(\time() + $expires_in);
|
||||
}
|
||||
$key->setServerSalt(\substr($new_nonce, 0, 8) ^ \substr($server_nonce, 0, 8));
|
||||
$key->setAuthKey($auth_key_str);
|
||||
|
||||
$this->logger->logger('Auth key generated', \danog\MadelineProto\Logger::NOTICE);
|
||||
|
||||
return $key;
|
||||
case 'dh_gen_retry':
|
||||
if ($Set_client_DH_params_answer['new_nonce_hash2'] != $new_nonce_hash2) {
|
||||
@ -433,7 +417,6 @@ trait AuthKeyHandler
|
||||
throw new \danog\MadelineProto\SecurityException('Auth Failed');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check validity of g_a parameters.
|
||||
*
|
||||
@ -459,10 +442,8 @@ trait AuthKeyHandler
|
||||
if ($g_a->compare(\danog\MadelineProto\Magic::$twoe1984) < 0 || $g_a->compare($p->subtract(\danog\MadelineProto\Magic::$twoe1984)) >= 0) {
|
||||
throw new \danog\MadelineProto\SecurityException('g_a is invalid (2^1984 < g_a < p - 2^1984 is false).');
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check validity of p and g parameters.
|
||||
*
|
||||
@ -515,10 +496,8 @@ trait AuthKeyHandler
|
||||
if ($g->compare(\danog\MadelineProto\Magic::$one) <= 0 || $g->compare($p->subtract(\danog\MadelineProto\Magic::$one)) >= 0) {
|
||||
throw new \danog\MadelineProto\SecurityException('g is invalid (1 < g < p - 1 is false).');
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get diffie-hellman configuration.
|
||||
*
|
||||
@ -531,16 +510,13 @@ trait AuthKeyHandler
|
||||
$dh_config = yield $this->methodCallAsyncRead('messages.getDhConfig', ['version' => $this->dh_config['version'], 'random_length' => 0], ['datacenter' => $this->datacenter->curdc]);
|
||||
if ($dh_config['_'] === 'messages.dhConfigNotModified') {
|
||||
$this->logger->logger('DH configuration not modified', \danog\MadelineProto\Logger::VERBOSE);
|
||||
|
||||
return $this->dh_config;
|
||||
}
|
||||
$dh_config['p'] = new \tgseclib\Math\BigInteger((string) $dh_config['p'], 256);
|
||||
$dh_config['g'] = new \tgseclib\Math\BigInteger($dh_config['g']);
|
||||
$this->checkPG($dh_config['p'], $dh_config['g']);
|
||||
|
||||
return $this->dh_config = $dh_config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind temporary and permanent auth keys.
|
||||
*
|
||||
@ -555,7 +531,6 @@ trait AuthKeyHandler
|
||||
{
|
||||
$datacenterConnection = $this->datacenter->getDataCenterConnection($datacenter);
|
||||
$connection = $datacenterConnection->getAuthConnection();
|
||||
|
||||
for ($retry_id_total = 1; $retry_id_total <= $this->settings['max_tries']['authorization']; $retry_id_total++) {
|
||||
try {
|
||||
$this->logger->logger('Binding authorization keys...', \danog\MadelineProto\Logger::VERBOSE);
|
||||
@ -577,7 +552,6 @@ trait AuthKeyHandler
|
||||
$this->logger->logger('Bound temporary and permanent authorization keys, DC ' . $datacenter, \danog\MadelineProto\Logger::NOTICE);
|
||||
$datacenterConnection->bind();
|
||||
$datacenterConnection->flush();
|
||||
|
||||
return true;
|
||||
}
|
||||
} catch (\danog\MadelineProto\SecurityException $e) {
|
||||
@ -588,10 +562,8 @@ trait AuthKeyHandler
|
||||
$this->logger->logger('An RPCErrorException occurred while generating the authorization key: ' . $e->getMessage() . ' Retrying (try number ' . $retry_id_total . ')...', \danog\MadelineProto\Logger::WARNING);
|
||||
}
|
||||
}
|
||||
|
||||
throw new \danog\MadelineProto\SecurityException('An error occurred while binding temporary and permanent authorization keys.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Factorize number asynchronously using the wolfram API.
|
||||
*
|
||||
@ -603,21 +575,10 @@ trait AuthKeyHandler
|
||||
{
|
||||
$code = yield $this->datacenter->fileGetContents('http://www.wolframalpha.com/api/v1/code');
|
||||
$query = 'Do prime factorization of ' . $what;
|
||||
$params = [
|
||||
'async' => true,
|
||||
'banners' => 'raw',
|
||||
'debuggingdata' => false,
|
||||
'format' => 'moutput',
|
||||
'formattimeout' => 8,
|
||||
'input' => $query,
|
||||
'output' => 'JSON',
|
||||
'proxycode' => \json_decode($code, true)['code'],
|
||||
];
|
||||
$params = ['async' => true, 'banners' => 'raw', 'debuggingdata' => false, 'format' => 'moutput', 'formattimeout' => 8, 'input' => $query, 'output' => 'JSON', 'proxycode' => \json_decode($code, true)['code']];
|
||||
$url = 'https://www.wolframalpha.com/input/json.jsp?' . \http_build_query($params);
|
||||
|
||||
$request = new Request($url);
|
||||
$request->setHeader('referer', 'https://www.wolframalpha.com/input/?i=' . \urlencode($query));
|
||||
|
||||
$res = \json_decode(yield (yield $this->datacenter->getHTTPClient()->request($request))->getBody()->buffer(), true);
|
||||
if (!isset($res['queryresult']['pods'])) {
|
||||
return false;
|
||||
@ -625,24 +586,20 @@ trait AuthKeyHandler
|
||||
$fres = false;
|
||||
foreach ($res['queryresult']['pods'] as $cur) {
|
||||
if ($cur['id'] === 'Divisors') {
|
||||
$fres = \explode(', ', \preg_replace(["/{\d+, /", "/, \d+}$/"], '', $cur['subpods'][0]['moutput']));
|
||||
$fres = \explode(', ', \preg_replace(["/{\\d+, /", "/, \\d+}\$/"], '', $cur['subpods'][0]['moutput']));
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (\is_array($fres)) {
|
||||
$fres = $fres[0];
|
||||
|
||||
$newval = \intval($fres);
|
||||
if (\is_int($newval)) {
|
||||
$fres = $newval;
|
||||
}
|
||||
|
||||
return $fres;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asynchronously create, bind and check auth keys for all DCs.
|
||||
*
|
||||
@ -658,9 +615,7 @@ trait AuthKeyHandler
|
||||
}
|
||||
$this->logger("Initing authorization...");
|
||||
$initing = $this->initing_authorization;
|
||||
|
||||
$this->initing_authorization = true;
|
||||
|
||||
try {
|
||||
$dcs = [];
|
||||
$postpone = [];
|
||||
@ -688,26 +643,22 @@ trait AuthKeyHandler
|
||||
$first = \array_shift($dcs)();
|
||||
yield $first;
|
||||
}
|
||||
|
||||
foreach ($dcs as $id => &$dc) {
|
||||
$dc = $dc();
|
||||
}
|
||||
yield \danog\MadelineProto\Tools::all($dcs);
|
||||
|
||||
foreach ($postpone as $id => $socket) {
|
||||
yield $this->initAuthorizationSocket($id, $socket);
|
||||
yield from $this->initAuthorizationSocket($id, $socket);
|
||||
}
|
||||
|
||||
if ($this->pending_auth && empty($this->init_auth_dcs)) {
|
||||
$this->pending_auth = false;
|
||||
yield $this->initAuthorization();
|
||||
yield from $this->initAuthorization();
|
||||
}
|
||||
} finally {
|
||||
$this->pending_auth = false;
|
||||
$this->initing_authorization = $initing;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Init auth keys for single DC.
|
||||
*
|
||||
@ -720,20 +671,17 @@ trait AuthKeyHandler
|
||||
*/
|
||||
public function initAuthorizationSocket(string $id, DataCenterConnection $socket): \Generator
|
||||
{
|
||||
$this->logger("Initing authorization DC $id...");
|
||||
$this->logger("Initing authorization DC {$id}...");
|
||||
$this->init_auth_dcs[$id] = true;
|
||||
$connection = $socket->getAuthConnection();
|
||||
|
||||
try {
|
||||
$socket->createSession();
|
||||
|
||||
$cdn = $socket->isCDN();
|
||||
$media = $socket->isMedia();
|
||||
|
||||
if (!$socket->hasTempAuthKey() || !$socket->hasPermAuthKey() || !$socket->isBound()) {
|
||||
if (!$socket->hasPermAuthKey() && !$cdn && !$media) {
|
||||
$this->logger->logger(\sprintf(\danog\MadelineProto\Lang::$current_lang['gen_perm_auth_key'], $id), \danog\MadelineProto\Logger::NOTICE);
|
||||
$socket->setPermAuthKey(yield $this->createAuthKey(-1, $id));
|
||||
$socket->setPermAuthKey(yield from $this->createAuthKey(-1, $id));
|
||||
//$socket->authorized(false);
|
||||
}
|
||||
if ($media) {
|
||||
@ -745,40 +693,35 @@ trait AuthKeyHandler
|
||||
if ($this->datacenter->getDataCenterConnection($id)->getSettings()['pfs']) {
|
||||
if (!$cdn) {
|
||||
$this->logger->logger(\sprintf(\danog\MadelineProto\Lang::$current_lang['gen_temp_auth_key'], $id), \danog\MadelineProto\Logger::NOTICE);
|
||||
|
||||
//$authorized = $socket->authorized;
|
||||
//$socket->authorized = false;
|
||||
|
||||
$socket->setTempAuthKey(null);
|
||||
$socket->setTempAuthKey(yield $this->createAuthKey($this->settings['authorization']['default_temp_auth_key_expires_in'], $id));
|
||||
yield $this->bindTempAuthKey($this->settings['authorization']['default_temp_auth_key_expires_in'], $id);
|
||||
|
||||
$socket->setTempAuthKey(yield from $this->createAuthKey($this->settings['authorization']['default_temp_auth_key_expires_in'], $id));
|
||||
yield from $this->bindTempAuthKey($this->settings['authorization']['default_temp_auth_key_expires_in'], $id);
|
||||
$this->config = yield $connection->methodCallAsyncRead('help.getConfig', []);
|
||||
|
||||
yield $this->syncAuthorization($id);
|
||||
yield from $this->syncAuthorization($id);
|
||||
} elseif (!$socket->hasTempAuthKey()) {
|
||||
$this->logger->logger(\sprintf(\danog\MadelineProto\Lang::$current_lang['gen_temp_auth_key'], $id), \danog\MadelineProto\Logger::NOTICE);
|
||||
$socket->setTempAuthKey(yield $this->createAuthKey($this->settings['authorization']['default_temp_auth_key_expires_in'], $id));
|
||||
$socket->setTempAuthKey(yield from $this->createAuthKey($this->settings['authorization']['default_temp_auth_key_expires_in'], $id));
|
||||
}
|
||||
} else {
|
||||
if (!$cdn) {
|
||||
$socket->bind(false);
|
||||
$this->config = yield $connection->methodCallAsyncRead('help.getConfig', []);
|
||||
yield $this->syncAuthorization($id);
|
||||
yield from $this->syncAuthorization($id);
|
||||
} elseif (!$socket->hasTempAuthKey()) {
|
||||
$this->logger->logger(\sprintf(\danog\MadelineProto\Lang::$current_lang['gen_temp_auth_key'], $id), \danog\MadelineProto\Logger::NOTICE);
|
||||
$socket->setTempAuthKey(yield $this->createAuthKey($this->settings['authorization']['default_temp_auth_key_expires_in'], $id));
|
||||
$socket->setTempAuthKey(yield from $this->createAuthKey($this->settings['authorization']['default_temp_auth_key_expires_in'], $id));
|
||||
}
|
||||
}
|
||||
} elseif (!$cdn) {
|
||||
yield $this->syncAuthorization($id);
|
||||
yield from $this->syncAuthorization($id);
|
||||
}
|
||||
} finally {
|
||||
$this->logger("Done initing authorization DC $id");
|
||||
$this->logger("Done initing authorization DC {$id}");
|
||||
unset($this->init_auth_dcs[$id]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sync authorization data between DCs.
|
||||
*
|
||||
|
@ -40,7 +40,6 @@ trait CallHandler
|
||||
{
|
||||
return \danog\MadelineProto\Tools::wait($this->methodCallAsyncRead($method, $args, $aargs));
|
||||
}
|
||||
|
||||
/**
|
||||
* Call method and wait asynchronously for response.
|
||||
*
|
||||
@ -54,16 +53,13 @@ trait CallHandler
|
||||
*/
|
||||
public function methodCallAsyncRead(string $method, $args = [], array $aargs = ['msg_id' => null]): Promise
|
||||
{
|
||||
$deferred = new Deferred;
|
||||
$this->datacenter->waitGetConnection($aargs['datacenter'] ?? $this->datacenter->curdc)->onResolve(
|
||||
static function ($e, $res) use (&$method, &$args, &$aargs, &$deferred) {
|
||||
$deferred = new Deferred();
|
||||
$this->datacenter->waitGetConnection($aargs['datacenter'] ?? $this->datacenter->curdc)->onResolve(static function ($e, $res) use (&$method, &$args, &$aargs, &$deferred) {
|
||||
if ($e) {
|
||||
throw $e;
|
||||
}
|
||||
$deferred->resolve($res->methodCallAsyncRead($method, $args, $aargs));
|
||||
}
|
||||
);
|
||||
|
||||
});
|
||||
return $deferred->promise();
|
||||
}
|
||||
/**
|
||||
@ -77,16 +73,13 @@ trait CallHandler
|
||||
*/
|
||||
public function methodCallAsyncWrite(string $method, $args = [], array $aargs = ['msg_id' => null]): Promise
|
||||
{
|
||||
$deferred = new Deferred;
|
||||
$this->datacenter->waitGetConnection($aargs['datacenter'] ?? $this->datacenter->curdc)->onResolve(
|
||||
static function ($e, $res) use (&$method, &$args, &$aargs, &$deferred) {
|
||||
$deferred = new Deferred();
|
||||
$this->datacenter->waitGetConnection($aargs['datacenter'] ?? $this->datacenter->curdc)->onResolve(static function ($e, $res) use (&$method, &$args, &$aargs, &$deferred) {
|
||||
if ($e) {
|
||||
throw $e;
|
||||
}
|
||||
$deferred->resolve($res->methodCallAsyncWrite($method, $args, $aargs));
|
||||
}
|
||||
);
|
||||
|
||||
});
|
||||
return $deferred->promise();
|
||||
}
|
||||
}
|
||||
|
@ -30,7 +30,6 @@ class CombinedUpdatesState
|
||||
* @var array<UpdatesState>
|
||||
*/
|
||||
private $states = [];
|
||||
|
||||
/**
|
||||
* Constructor function.
|
||||
*
|
||||
@ -49,7 +48,6 @@ class CombinedUpdatesState
|
||||
$this->states[$channel] = $state;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or update multiple parameters.
|
||||
*
|
||||
@ -66,10 +64,8 @@ class CombinedUpdatesState
|
||||
if (!isset($this->states[$channel])) {
|
||||
return $this->states[$channel] = new UpdatesState($init, $channel);
|
||||
}
|
||||
|
||||
return $this->states[$channel]->update($init);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove update state.
|
||||
*
|
||||
@ -83,7 +79,6 @@ class CombinedUpdatesState
|
||||
unset($this->states[$channel]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if update state is present.
|
||||
*
|
||||
@ -95,7 +90,6 @@ class CombinedUpdatesState
|
||||
{
|
||||
return isset($this->states[$channel]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Are we currently busy?
|
||||
*
|
||||
|
@ -39,10 +39,8 @@ trait Crypt
|
||||
$sha256_b = \hash('sha256', \substr($auth_key, 40 + $x, 36) . $msg_key, true);
|
||||
$aes_key = \substr($sha256_a, 0, 8) . \substr($sha256_b, 8, 16) . \substr($sha256_a, 24, 8);
|
||||
$aes_iv = \substr($sha256_b, 0, 8) . \substr($sha256_a, 8, 16) . \substr($sha256_b, 24, 8);
|
||||
|
||||
return [$aes_key, $aes_iv];
|
||||
}
|
||||
|
||||
/**
|
||||
* AES KDF function for MTProto v1.
|
||||
*
|
||||
@ -63,10 +61,8 @@ trait Crypt
|
||||
$sha1_d = \sha1($msg_key . \substr($auth_key, 96 + $x, 32), true);
|
||||
$aes_key = \substr($sha1_a, 0, 8) . \substr($sha1_b, 8, 12) . \substr($sha1_c, 4, 12);
|
||||
$aes_iv = \substr($sha1_a, 8, 12) . \substr($sha1_b, 0, 8) . \substr($sha1_c, 16, 4) . \substr($sha1_d, 0, 8);
|
||||
|
||||
return [$aes_key, $aes_iv];
|
||||
}
|
||||
|
||||
/**
|
||||
* CTR encrypt.
|
||||
*
|
||||
@ -83,10 +79,8 @@ trait Crypt
|
||||
$cipher = new \tgseclib\Crypt\AES('ctr');
|
||||
$cipher->setKey($key);
|
||||
$cipher->setIV($iv);
|
||||
|
||||
return @$cipher->encrypt($message);
|
||||
}
|
||||
|
||||
/**
|
||||
* IGE encrypt.
|
||||
*
|
||||
@ -103,7 +97,6 @@ trait Crypt
|
||||
$cipher = new \tgseclib\Crypt\AES('ige');
|
||||
$cipher->setKey($key);
|
||||
$cipher->setIV($iv);
|
||||
|
||||
return @$cipher->encrypt($message);
|
||||
}
|
||||
/**
|
||||
@ -122,7 +115,6 @@ trait Crypt
|
||||
$cipher = new \tgseclib\Crypt\AES('ige');
|
||||
$cipher->setKey($key);
|
||||
$cipher->setIV($iv);
|
||||
|
||||
return @$cipher->decrypt($message);
|
||||
}
|
||||
}
|
||||
|
@ -62,17 +62,16 @@ trait Files
|
||||
$cb = $file;
|
||||
$file = $file->getFile();
|
||||
}
|
||||
if (\is_string($file) || (\is_object($file) && \method_exists($file, '__toString'))) {
|
||||
if (\is_string($file) || \is_object($file) && \method_exists($file, '__toString')) {
|
||||
if (\filter_var($file, FILTER_VALIDATE_URL)) {
|
||||
return yield $this->uploadFromUrl($file, 0, $fileName, $cb, $encrypted);
|
||||
return yield from $this->uploadFromUrl($file, 0, $fileName, $cb, $encrypted);
|
||||
}
|
||||
} elseif (\is_array($file)) {
|
||||
return yield $this->uploadFromTgfile($file, $cb, $encrypted);
|
||||
return yield from $this->uploadFromTgfile($file, $cb, $encrypted);
|
||||
}
|
||||
if (!$this->settings['upload']['allow_automatic_upload']) {
|
||||
return yield $this->uploadFromUrl($file, 0, $fileName, $cb, $encrypted);
|
||||
return yield from $this->uploadFromUrl($file, 0, $fileName, $cb, $encrypted);
|
||||
}
|
||||
|
||||
$file = \danog\MadelineProto\Absolute::absolute($file);
|
||||
if (!yield exists($file)) {
|
||||
throw new \danog\MadelineProto\Exception(\danog\MadelineProto\Lang::$current_lang['file_not_exist']);
|
||||
@ -80,19 +79,15 @@ trait Files
|
||||
if (empty($fileName)) {
|
||||
$fileName = \basename($file);
|
||||
}
|
||||
|
||||
StatCache::clear($file);
|
||||
|
||||
$size = (yield \stat($file))['size'];
|
||||
if ($size > 512 * 1024 * 3000) {
|
||||
throw new \danog\MadelineProto\Exception('Given file is too big!');
|
||||
}
|
||||
|
||||
$stream = yield open($file, 'rb');
|
||||
$mime = $this->getMimeFromFile($file);
|
||||
|
||||
try {
|
||||
return yield $this->uploadFromStream($stream, $size, $mime, $fileName, $cb, $encrypted);
|
||||
return yield from $this->uploadFromStream($stream, $size, $mime, $fileName, $cb, $encrypted);
|
||||
} finally {
|
||||
yield $stream->close();
|
||||
}
|
||||
@ -119,20 +114,17 @@ trait Files
|
||||
$request->setTransferTimeout(10 * 1000 * 3600);
|
||||
$request->setBodySizeLimit(512 * 1024 * 3000);
|
||||
$response = yield $this->datacenter->getHTTPClient()->request($request);
|
||||
if (200 !== $status = $response->getStatus()) {
|
||||
throw new Exception("Wrong status code: $status ".$response->getReason());
|
||||
if (200 !== ($status = $response->getStatus())) {
|
||||
throw new Exception("Wrong status code: {$status} " . $response->getReason());
|
||||
}
|
||||
$mime = \trim(\explode(';', $response->getHeader('content-type') ?? 'application/octet-stream')[0]);
|
||||
$size = $response->getHeader('content-length') ?? $size;
|
||||
|
||||
$stream = $response->getBody();
|
||||
if (!$size) {
|
||||
$this->logger->logger("No content length for $url, caching first");
|
||||
|
||||
$this->logger->logger("No content length for {$url}, caching first");
|
||||
$body = $stream;
|
||||
$stream = new BlockingFile(\fopen('php://temp', 'r+b'), 'php://temp', 'r+b');
|
||||
|
||||
while (null !== $chunk = yield $body->read()) {
|
||||
while (null !== ($chunk = yield $body->read())) {
|
||||
yield $stream->write($chunk);
|
||||
}
|
||||
$size = $stream->tell();
|
||||
@ -141,8 +133,7 @@ trait Files
|
||||
}
|
||||
yield $stream->seek(0);
|
||||
}
|
||||
|
||||
return yield $this->uploadFromStream($stream, $size, $mime, $fileName, $cb, $encrypted);
|
||||
return yield from $this->uploadFromStream($stream, $size, $mime, $fileName, $cb, $encrypted);
|
||||
}
|
||||
/**
|
||||
* Upload file from stream.
|
||||
@ -162,7 +153,6 @@ trait Files
|
||||
$cb = $stream;
|
||||
$stream = $stream->getFile();
|
||||
}
|
||||
|
||||
/* @var $stream \Amp\ByteStream\OutputStream */
|
||||
if (!\is_object($stream)) {
|
||||
$stream = new ResourceOutputStream($stream);
|
||||
@ -178,11 +168,9 @@ trait Files
|
||||
} catch (StreamException $e) {
|
||||
}
|
||||
}
|
||||
|
||||
$created = false;
|
||||
|
||||
if ($stream instanceof Handle) {
|
||||
$callable = static function (int $offset, int $size) use ($stream, $seekable) {
|
||||
$callable = static function (int $offset, int $size) use ($stream, $seekable): \Generator {
|
||||
if ($seekable) {
|
||||
while ($stream->tell() !== $offset) {
|
||||
yield $stream->seek($offset);
|
||||
@ -192,13 +180,11 @@ trait Files
|
||||
};
|
||||
} else {
|
||||
if (!$stream instanceof BufferedRawStream) {
|
||||
$ctx = (new ConnectionContext)
|
||||
->addStream(PremadeStream::getName(), $stream)
|
||||
->addStream(SimpleBufferedRawStream::getName());
|
||||
$ctx = (new ConnectionContext())->addStream(PremadeStream::getName(), $stream)->addStream(SimpleBufferedRawStream::getName());
|
||||
$stream = yield $ctx->getStream();
|
||||
$created = true;
|
||||
}
|
||||
$callable = static function (int $offset, int $size) use ($stream) {
|
||||
$callable = static function (int $offset, int $size) use ($stream): \Generator {
|
||||
$reader = yield $stream->getReadBuffer($l);
|
||||
try {
|
||||
return yield $reader->bufferRead($size);
|
||||
@ -209,8 +195,7 @@ trait Files
|
||||
};
|
||||
$seekable = false;
|
||||
}
|
||||
|
||||
$res = yield $this->uploadFromCallable($callable, $size, $mime, $fileName, $cb, $seekable, $encrypted);
|
||||
$res = (yield from $this->uploadFromCallable($callable, $size, $mime, $fileName, $cb, $seekable, $encrypted));
|
||||
if ($created) {
|
||||
$stream->disconnect();
|
||||
}
|
||||
@ -246,21 +231,17 @@ trait Files
|
||||
$this->logger->logger('Upload status: ' . $percent . '%', \danog\MadelineProto\Logger::NOTICE);
|
||||
};
|
||||
}
|
||||
|
||||
$datacenter = $this->settings['connection_settings']['default_dc'];
|
||||
if ($this->datacenter->has($datacenter . '_media')) {
|
||||
$datacenter .= '_media';
|
||||
}
|
||||
|
||||
$part_size = $this->settings['upload']['part_size'];
|
||||
$parallel_chunks = $this->settings['upload']['parallel_chunks'] ? $this->settings['upload']['parallel_chunks'] : 3000;
|
||||
|
||||
$part_total_num = (int) \ceil($size / $part_size);
|
||||
$part_num = 0;
|
||||
$method = $size > 10 * 1024 * 1024 ? 'upload.saveBigFilePart' : 'upload.saveFilePart';
|
||||
$constructor = 'input' . ($encrypted === true ? 'Encrypted' : '') . ($size > 10 * 1024 * 1024 ? 'FileBig' : 'File') . ($encrypted === true ? 'Uploaded' : '');
|
||||
$file_id = \danog\MadelineProto\Tools::random(8);
|
||||
|
||||
$ige = null;
|
||||
if ($encrypted === true) {
|
||||
$key = \danog\MadelineProto\Tools::random(32);
|
||||
@ -275,7 +256,6 @@ trait Files
|
||||
}
|
||||
//$ctx = \hash_init('md5');
|
||||
$promises = [];
|
||||
|
||||
$speed = 0;
|
||||
$time = 0;
|
||||
$cb = function () use ($cb, $part_total_num, &$speed, &$time) {
|
||||
@ -283,35 +263,25 @@ trait Files
|
||||
$cur++;
|
||||
\danog\MadelineProto\Tools::callFork($cb($cur * 100 / $part_total_num, $speed, $time));
|
||||
};
|
||||
|
||||
$callable = static function (int $part_num) use ($file_id, $part_total_num, $part_size, $callable, $ige) {
|
||||
$callable = static function (int $part_num) use ($file_id, $part_total_num, $part_size, $callable, $ige): \Generator {
|
||||
$bytes = yield $callable($part_num * $part_size, $part_size);
|
||||
|
||||
if ($ige) {
|
||||
$bytes = $ige->encrypt(\str_pad($bytes, $part_size, \chr(0)));
|
||||
}
|
||||
//\hash_update($ctx, $bytes);
|
||||
|
||||
return ['file_id' => $file_id, 'file_part' => $part_num, 'file_total_parts' => $part_total_num, 'bytes' => $bytes];
|
||||
};
|
||||
|
||||
$resPromises = [];
|
||||
$exception = null;
|
||||
|
||||
$start = \microtime(true);
|
||||
while ($part_num < $part_total_num) {
|
||||
$writePromise = $this->methodCallAsyncWrite(
|
||||
$method,
|
||||
$callable($part_num),
|
||||
['heavy' => true, 'file' => true, 'datacenter' => &$datacenter]
|
||||
);
|
||||
$writePromise = $this->methodCallAsyncWrite($method, $callable($part_num), ['heavy' => true, 'file' => true, 'datacenter' => &$datacenter]);
|
||||
if (!$seekable) {
|
||||
yield $writePromise;
|
||||
}
|
||||
$writePromise->onResolve(
|
||||
function ($e, $readDeferred) use ($cb, $part_num, &$resPromises, &$exception) {
|
||||
$writePromise->onResolve(function ($e, $readDeferred) use ($cb, $part_num, &$resPromises, &$exception): \Generator {
|
||||
if ($e) {
|
||||
$this->logger("Got exception while uploading: $e");
|
||||
$this->logger("Got exception while uploading: {$e}");
|
||||
$exception = $e;
|
||||
return;
|
||||
}
|
||||
@ -324,47 +294,41 @@ trait Files
|
||||
// Got OK from server for chunk!
|
||||
$cb();
|
||||
} catch (\Throwable $e) {
|
||||
$this->logger("Got exception while uploading: $e");
|
||||
$this->logger("Got exception while uploading: {$e}");
|
||||
$exception = $e;
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
$promises[] = $writePromise;
|
||||
++$part_num;
|
||||
|
||||
if (!($part_num % $parallel_chunks)) { // By default, 10 mb at a time, for a typical bandwidth of 1gbps (run the code in this every second)
|
||||
if (!($part_num % $parallel_chunks)) {
|
||||
// By default, 10 mb at a time, for a typical bandwidth of 1gbps (run the code in this every second)
|
||||
yield Tools::all($promises);
|
||||
$promises = [];
|
||||
if ($exception) {
|
||||
throw $exception;
|
||||
}
|
||||
|
||||
$time = \microtime(true) - $start;
|
||||
$speed = (int) (($size * 8) / $time) / 1000000;
|
||||
$this->logger->logger("Partial upload time: $time");
|
||||
$this->logger->logger("Partial upload speed: $speed mbps");
|
||||
$speed = (int) ($size * 8 / $time) / 1000000;
|
||||
$this->logger->logger("Partial upload time: {$time}");
|
||||
$this->logger->logger("Partial upload speed: {$speed} mbps");
|
||||
}
|
||||
}
|
||||
|
||||
yield all($promises);
|
||||
yield all($resPromises);
|
||||
|
||||
$time = \microtime(true) - $start;
|
||||
$speed = (int) (($size * 8) / $time) / 1000000;
|
||||
$this->logger->logger("Total upload time: $time");
|
||||
$this->logger->logger("Total upload speed: $speed mbps");
|
||||
|
||||
$speed = (int) ($size * 8 / $time) / 1000000;
|
||||
$this->logger->logger("Total upload time: {$time}");
|
||||
$this->logger->logger("Total upload speed: {$speed} mbps");
|
||||
$constructor = ['_' => $constructor, 'id' => $file_id, 'parts' => $part_total_num, 'name' => $fileName, 'mime_type' => $mime];
|
||||
if ($encrypted === true) {
|
||||
$constructor['key_fingerprint'] = $fingerprint;
|
||||
$constructor['key'] = $key;
|
||||
$constructor['iv'] = $iv;
|
||||
}
|
||||
$constructor['md5_checksum'] = ''; //\hash_final($ctx);
|
||||
|
||||
$constructor['md5_checksum'] = '';
|
||||
//\hash_final($ctx);
|
||||
return $constructor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Upload file to secret chat.
|
||||
*
|
||||
@ -378,7 +342,6 @@ trait Files
|
||||
{
|
||||
return $this->upload($file, $fileName, $cb, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reupload telegram file.
|
||||
*
|
||||
@ -394,15 +357,13 @@ trait Files
|
||||
$cb = $media;
|
||||
$media = $media->getFile();
|
||||
}
|
||||
$media = yield $this->getDownloadInfo($media);
|
||||
$media = (yield from $this->getDownloadInfo($media));
|
||||
if (!isset($media['size'], $media['mime'])) {
|
||||
throw new Exception('Wrong file provided!');
|
||||
}
|
||||
$size = $media['size'];
|
||||
$mime = $media['mime'];
|
||||
|
||||
$chunk_size = $this->settings['upload']['part_size'];
|
||||
|
||||
$bridge = new class($size, $chunk_size, $cb) {
|
||||
/**
|
||||
* Read promises.
|
||||
@ -440,7 +401,6 @@ trait Files
|
||||
* @var ?callable
|
||||
*/
|
||||
private $cb;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
@ -451,8 +411,8 @@ trait Files
|
||||
public function __construct(int $size, int $partSize, ?callable $cb)
|
||||
{
|
||||
for ($x = 0; $x < $size; $x += $partSize) {
|
||||
$this->read []= new Deferred;
|
||||
$this->write []= new Deferred;
|
||||
$this->read[] = new Deferred();
|
||||
$this->write[] = new Deferred();
|
||||
$this->wrote[] = $size - $x < $partSize ? $size - $x : $partSize;
|
||||
}
|
||||
$this->partSize = $partSize;
|
||||
@ -504,16 +464,12 @@ trait Files
|
||||
$reader = [$bridge, 'read'];
|
||||
$writer = [$bridge, 'write'];
|
||||
$cb = [$bridge, 'callback'];
|
||||
|
||||
$read = $this->uploadFromCallable($reader, $size, $mime, '', $cb, true, $encrypted);
|
||||
$write = $this->downloadToCallable($media, $writer, null, true, 0, -1, $chunk_size);
|
||||
|
||||
list($res) = yield \danog\MadelineProto\Tools::all([$read, $write]);
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
private function genAllFile($media)
|
||||
private function genAllFile($media): \Generator
|
||||
{
|
||||
$res = [$this->TL->getConstructors()->findByPredicate($media['_'])['type'] => $media];
|
||||
switch ($media['_']) {
|
||||
@ -531,15 +487,7 @@ trait Files
|
||||
throw new \danog\MadelineProto\Exception('No access hash');
|
||||
}
|
||||
$res['Photo'] = $media['photo'];
|
||||
$res['InputPhoto'] = [
|
||||
'_' => 'inputPhoto',
|
||||
'id' => $media['photo']['id'],
|
||||
'access_hash' => $media['photo']['access_hash'],
|
||||
'file_reference' => yield $this->referenceDatabase->getReference(
|
||||
ReferenceDatabase::PHOTO_LOCATION,
|
||||
$media['photo']
|
||||
),
|
||||
];
|
||||
$res['InputPhoto'] = ['_' => 'inputPhoto', 'id' => $media['photo']['id'], 'access_hash' => $media['photo']['access_hash'], 'file_reference' => yield $this->referenceDatabase->getReference(ReferenceDatabase::PHOTO_LOCATION, $media['photo'])];
|
||||
$res['InputMedia'] = ['_' => 'inputMediaPhoto', 'id' => $res['InputPhoto']];
|
||||
if (isset($media['ttl_seconds'])) {
|
||||
$res['InputMedia']['ttl_seconds'] = $media['ttl_seconds'];
|
||||
@ -550,15 +498,7 @@ trait Files
|
||||
throw new \danog\MadelineProto\Exception('No access hash');
|
||||
}
|
||||
$res['Document'] = $media['document'];
|
||||
$res['InputDocument'] = [
|
||||
'_' => 'inputDocument',
|
||||
'id' => $media['document']['id'],
|
||||
'access_hash' => $media['document']['access_hash'],
|
||||
'file_reference' => yield $this->referenceDatabase->getReference(
|
||||
ReferenceDatabase::DOCUMENT_LOCATION,
|
||||
$media['document']
|
||||
),
|
||||
];
|
||||
$res['InputDocument'] = ['_' => 'inputDocument', 'id' => $media['document']['id'], 'access_hash' => $media['document']['access_hash'], 'file_reference' => yield $this->referenceDatabase->getReference(ReferenceDatabase::DOCUMENT_LOCATION, $media['document'])];
|
||||
$res['InputMedia'] = ['_' => 'inputMediaDocument', 'id' => $res['InputDocument']];
|
||||
if (isset($media['ttl_seconds'])) {
|
||||
$res['InputMedia']['ttl_seconds'] = $media['ttl_seconds'];
|
||||
@ -571,15 +511,7 @@ trait Files
|
||||
if (!isset($media['access_hash'])) {
|
||||
throw new \danog\MadelineProto\Exception('No access hash');
|
||||
}
|
||||
$res['InputDocument'] = [
|
||||
'_' => 'inputDocument',
|
||||
'id' => $media['id'],
|
||||
'access_hash' => $media['access_hash'],
|
||||
'file_reference' => yield $this->referenceDatabase->getReference(
|
||||
ReferenceDatabase::DOCUMENT_LOCATION,
|
||||
$media
|
||||
),
|
||||
];
|
||||
$res['InputDocument'] = ['_' => 'inputDocument', 'id' => $media['id'], 'access_hash' => $media['access_hash'], 'file_reference' => yield $this->referenceDatabase->getReference(ReferenceDatabase::DOCUMENT_LOCATION, $media)];
|
||||
$res['InputMedia'] = ['_' => 'inputMediaDocument', 'id' => $res['InputDocument']];
|
||||
$res['MessageMedia'] = ['_' => 'messageMediaDocument', 'document' => $media];
|
||||
break;
|
||||
@ -587,25 +519,15 @@ trait Files
|
||||
if (!isset($media['access_hash'])) {
|
||||
throw new \danog\MadelineProto\Exception('No access hash');
|
||||
}
|
||||
$res['InputPhoto'] = [
|
||||
'_' => 'inputPhoto',
|
||||
'id' => $media['id'],
|
||||
'access_hash' => $media['access_hash'],
|
||||
'file_reference' => yield $this->referenceDatabase->getReference(
|
||||
ReferenceDatabase::PHOTO_LOCATION,
|
||||
$media
|
||||
),
|
||||
];
|
||||
$res['InputPhoto'] = ['_' => 'inputPhoto', 'id' => $media['id'], 'access_hash' => $media['access_hash'], 'file_reference' => yield $this->referenceDatabase->getReference(ReferenceDatabase::PHOTO_LOCATION, $media)];
|
||||
$res['InputMedia'] = ['_' => 'inputMediaPhoto', 'id' => $res['InputPhoto']];
|
||||
$res['MessageMedia'] = ['_' => 'messageMediaPhoto', 'photo' => $media];
|
||||
break;
|
||||
default:
|
||||
throw new \danog\MadelineProto\Exception("Could not convert media object of type {$media['_']}");
|
||||
}
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get info about file.
|
||||
*
|
||||
@ -632,13 +554,11 @@ trait Files
|
||||
case 'updateEditMessage':
|
||||
case 'updateEditChannelMessage':
|
||||
$constructor = $constructor['message'];
|
||||
|
||||
// no break
|
||||
case 'message':
|
||||
$constructor = $constructor['media'];
|
||||
}
|
||||
|
||||
return yield $this->genAllFile($constructor);
|
||||
return yield from $this->genAllFile($constructor);
|
||||
}
|
||||
/**
|
||||
* Get download info of the propic of a user
|
||||
@ -655,7 +575,7 @@ trait Files
|
||||
*/
|
||||
public function getPropicInfo($data): \Generator
|
||||
{
|
||||
return yield $this->getDownloadInfo($this->chats[(yield $this->getInfo($data))['bot_api_id']]);
|
||||
return yield from $this->getDownloadInfo($this->chats[(yield $this->getInfo($data))['bot_api_id']]);
|
||||
}
|
||||
/**
|
||||
* Get download info of file
|
||||
@ -690,15 +610,14 @@ trait Files
|
||||
$message_media = $message_media['message'];
|
||||
// no break
|
||||
case 'message':
|
||||
return yield $this->getDownloadInfo($message_media['media']);
|
||||
return yield from $this->getDownloadInfo($message_media['media']);
|
||||
case 'updateNewEncryptedMessage':
|
||||
$message_media = $message_media['message'];
|
||||
|
||||
// Secret media
|
||||
// no break
|
||||
case 'encryptedMessage':
|
||||
if ($message_media['decrypted_message']['media']['_'] === 'decryptedMessageMediaExternalDocument') {
|
||||
return yield $this->getDownloadInfo($message_media['decrypted_message']['media']);
|
||||
return yield from $this->getDownloadInfo($message_media['decrypted_message']['media']);
|
||||
}
|
||||
$res['InputFileLocation'] = ['_' => 'inputEncryptedFileLocation', 'id' => $message_media['file']['id'], 'access_hash' => $message_media['file']['access_hash'], 'dc_id' => $message_media['file']['dc_id']];
|
||||
$res['size'] = $message_media['decrypted_message']['media']['size'];
|
||||
@ -748,7 +667,6 @@ trait Files
|
||||
if (!isset($res['name']) || $res['name'] === '') {
|
||||
$res['name'] = Tools::unpackSignedLongString($message_media['file']['access_hash']);
|
||||
}
|
||||
|
||||
return $res;
|
||||
// Wallpapers
|
||||
case 'wallPaper':
|
||||
@ -762,118 +680,76 @@ trait Files
|
||||
$res['MessageMedia'] = $message_media;
|
||||
$message_media = $message_media['photo'];
|
||||
$size = \end($message_media['sizes']);
|
||||
|
||||
$res = \array_merge($res, yield $this->getDownloadInfo($size));
|
||||
|
||||
$res['InputFileLocation'] = [
|
||||
'_' => 'inputPhotoFileLocation',
|
||||
'thumb_size' => $res['thumb_size'] ?? 'x',
|
||||
'dc_id' => $message_media['dc_id'],
|
||||
'access_hash' => $message_media['access_hash'],
|
||||
'id' => $message_media['id'],
|
||||
'file_reference' => yield $this->referenceDatabase->getReference(
|
||||
ReferenceDatabase::PHOTO_LOCATION,
|
||||
$message_media
|
||||
),
|
||||
];
|
||||
|
||||
$res = \array_merge($res, yield from $this->getDownloadInfo($size));
|
||||
$res['InputFileLocation'] = ['_' => 'inputPhotoFileLocation', 'thumb_size' => $res['thumb_size'] ?? 'x', 'dc_id' => $message_media['dc_id'], 'access_hash' => $message_media['access_hash'], 'id' => $message_media['id'], 'file_reference' => yield $this->referenceDatabase->getReference(ReferenceDatabase::PHOTO_LOCATION, $message_media)];
|
||||
return $res;
|
||||
case 'user':
|
||||
case 'folder':
|
||||
case 'channel':
|
||||
case 'chat':
|
||||
case 'updateUserPhoto':
|
||||
$res = yield $this->getDownloadInfo($message_media['photo']);
|
||||
|
||||
if (\is_array($message_media) && ($message_media['min'] ?? false) && isset($message_media['access_hash'])) { // bot API file ID
|
||||
$res = (yield from $this->getDownloadInfo($message_media['photo']));
|
||||
if (\is_array($message_media) && ($message_media['min'] ?? false) && isset($message_media['access_hash'])) {
|
||||
// bot API file ID
|
||||
$message_media['min'] = false;
|
||||
$peer = $this->genAll($message_media)['InputPeer'];
|
||||
} else {
|
||||
$peer = (yield $this->getInfo($message_media))['InputPeer'];
|
||||
}
|
||||
$res['InputFileLocation'] = [
|
||||
'_' => 'inputPeerPhotoFileLocation',
|
||||
'big' => $res['big'],
|
||||
'dc_id' => $res['InputFileLocation']['dc_id'],
|
||||
'peer' => $peer,
|
||||
'volume_id' => $res['InputFileLocation']['volume_id'],
|
||||
'local_id' => $res['InputFileLocation']['local_id'],
|
||||
// The peer field will be added later
|
||||
];
|
||||
$res['InputFileLocation'] = ['_' => 'inputPeerPhotoFileLocation', 'big' => $res['big'], 'dc_id' => $res['InputFileLocation']['dc_id'], 'peer' => $peer, 'volume_id' => $res['InputFileLocation']['volume_id'], 'local_id' => $res['InputFileLocation']['local_id']];
|
||||
return $res;
|
||||
|
||||
case 'userProfilePhoto':
|
||||
case 'chatPhoto':
|
||||
$size = $message_media['photo_big'] ?? $message_media['photo_small'];
|
||||
|
||||
$res = yield $this->getDownloadInfo($size);
|
||||
$res = (yield from $this->getDownloadInfo($size));
|
||||
$res['big'] = isset($message_media['photo_big']);
|
||||
$res['InputFileLocation']['dc_id'] = $message_media['dc_id'];
|
||||
|
||||
return $res;
|
||||
case 'photoStrippedSize':
|
||||
$res['size'] = \strlen($message_media['bytes']);
|
||||
$res['data'] = $message_media['bytes'];
|
||||
$res['thumb_size'] = 'JPG';
|
||||
return $res;
|
||||
|
||||
case 'photoCachedSize':
|
||||
$res['size'] = \strlen($message_media['bytes']);
|
||||
$res['data'] = $message_media['bytes'];
|
||||
//$res['thumb_size'] = $res['data'];
|
||||
$res['thumb_size'] = $message_media['type'];
|
||||
|
||||
if ($message_media['location']['_'] === 'fileLocationUnavailable') {
|
||||
$res['name'] = Tools::unpackSignedLongString($message_media['volume_id']) . '_' . $message_media['local_id'];
|
||||
$res['mime'] = $this->getMimeFromBuffer($res['data']);
|
||||
$res['ext'] = $this->getExtensionFromMime($res['mime']);
|
||||
} else {
|
||||
$res = \array_merge($res, yield $this->getDownloadInfo($message_media['location']));
|
||||
$res = \array_merge($res, yield from $this->getDownloadInfo($message_media['location']));
|
||||
}
|
||||
|
||||
return $res;
|
||||
case 'photoSize':
|
||||
$res = yield $this->getDownloadInfo($message_media['location']);
|
||||
|
||||
$res = (yield from $this->getDownloadInfo($message_media['location']));
|
||||
$res['thumb_size'] = $message_media['type'];
|
||||
//$res['thumb_size'] = $size;
|
||||
if (isset($message_media['size'])) {
|
||||
$res['size'] = $message_media['size'];
|
||||
}
|
||||
|
||||
return $res;
|
||||
|
||||
case 'fileLocationUnavailable':
|
||||
throw new \danog\MadelineProto\Exception('File location unavailable');
|
||||
case 'fileLocation':
|
||||
$res['name'] = Tools::unpackSignedLongString($message_media['volume_id']) . '_' . $message_media['local_id'];
|
||||
$res['InputFileLocation'] = [
|
||||
'_' => 'inputFileLocation',
|
||||
'volume_id' => $message_media['volume_id'],
|
||||
'local_id' => $message_media['local_id'],
|
||||
'secret' => $message_media['secret'],
|
||||
'dc_id' => $message_media['dc_id'],
|
||||
'file_reference' => yield $this->referenceDatabase->getReference(
|
||||
ReferenceDatabase::PHOTO_LOCATION_LOCATION,
|
||||
$message_media
|
||||
),
|
||||
];
|
||||
$res['InputFileLocation'] = ['_' => 'inputFileLocation', 'volume_id' => $message_media['volume_id'], 'local_id' => $message_media['local_id'], 'secret' => $message_media['secret'], 'dc_id' => $message_media['dc_id'], 'file_reference' => yield $this->referenceDatabase->getReference(ReferenceDatabase::PHOTO_LOCATION_LOCATION, $message_media)];
|
||||
$res['ext'] = $this->getExtensionFromLocation($res['InputFileLocation'], '.jpg');
|
||||
$res['mime'] = $this->getMimeFromExtension($res['ext'], 'image/jpeg');
|
||||
|
||||
return $res;
|
||||
case 'fileLocationToBeDeprecated':
|
||||
$res['name'] = Tools::unpackSignedLongString($message_media['volume_id']) . '_' . $message_media['local_id'];
|
||||
$res['ext'] = '.jpg';
|
||||
$res['mime'] = $this->getMimeFromExtension($res['ext'], 'image/jpeg');
|
||||
$res['InputFileLocation'] = [
|
||||
'_' => 'inputFileLocationTemp', // Will be overwritten
|
||||
'_' => 'inputFileLocationTemp',
|
||||
// Will be overwritten
|
||||
'volume_id' => $message_media['volume_id'],
|
||||
'local_id' => $message_media['local_id'],
|
||||
];
|
||||
|
||||
return $res;
|
||||
|
||||
// Documents
|
||||
case 'decryptedMessageMediaExternalDocument':
|
||||
case 'document':
|
||||
@ -881,7 +757,6 @@ trait Files
|
||||
// no break
|
||||
case 'messageMediaDocument':
|
||||
$res['MessageMedia'] = $message_media;
|
||||
|
||||
foreach ($message_media['document']['attributes'] as $attribute) {
|
||||
switch ($attribute['_']) {
|
||||
case 'documentAttributeFilename':
|
||||
@ -902,19 +777,7 @@ trait Files
|
||||
$res['name'] .= ' - ' . $audio['performer'];
|
||||
}
|
||||
}
|
||||
|
||||
$res['InputFileLocation'] = [
|
||||
'_' => 'inputDocumentFileLocation',
|
||||
'id' => $message_media['document']['id'],
|
||||
'access_hash' => $message_media['document']['access_hash'],
|
||||
'version' => isset($message_media['document']['version']) ? $message_media['document']['version'] : 0,
|
||||
'dc_id' => $message_media['document']['dc_id'],
|
||||
'file_reference' => yield $this->referenceDatabase->getReference(
|
||||
ReferenceDatabase::DOCUMENT_LOCATION,
|
||||
$message_media['document']
|
||||
),
|
||||
];
|
||||
|
||||
$res['InputFileLocation'] = ['_' => 'inputDocumentFileLocation', 'id' => $message_media['document']['id'], 'access_hash' => $message_media['document']['access_hash'], 'version' => isset($message_media['document']['version']) ? $message_media['document']['version'] : 0, 'dc_id' => $message_media['document']['dc_id'], 'file_reference' => yield $this->referenceDatabase->getReference(ReferenceDatabase::DOCUMENT_LOCATION, $message_media['document'])];
|
||||
if (!isset($res['ext']) || $res['ext'] === '') {
|
||||
$res['ext'] = $this->getExtensionFromLocation($res['InputFileLocation'], $this->getExtensionFromMime($message_media['document']['mime_type']));
|
||||
}
|
||||
@ -926,7 +789,6 @@ trait Files
|
||||
}
|
||||
$res['name'] .= '_' . $message_media['document']['id'];
|
||||
$res['mime'] = $message_media['document']['mime_type'];
|
||||
|
||||
return $res;
|
||||
default:
|
||||
throw new \danog\MadelineProto\Exception('Invalid constructor provided: ' . $message_media['_']);
|
||||
@ -1052,12 +914,9 @@ trait Files
|
||||
$cb = $dir;
|
||||
$dir = $dir->getFile();
|
||||
}
|
||||
|
||||
$message_media = yield $this->getDownloadInfo($message_media);
|
||||
|
||||
return yield $this->downloadToFile($message_media, $dir.'/'.$message_media['name'].$message_media['ext'], $cb);
|
||||
$message_media = (yield from $this->getDownloadInfo($message_media));
|
||||
return yield from $this->downloadToFile($message_media, $dir . '/' . $message_media['name'] . $message_media['ext'], $cb);
|
||||
}
|
||||
|
||||
/**
|
||||
* Download file.
|
||||
*
|
||||
@ -1078,25 +937,20 @@ trait Files
|
||||
yield \touch($file);
|
||||
}
|
||||
$file = \realpath($file);
|
||||
$message_media = yield $this->getDownloadInfo($message_media);
|
||||
|
||||
$message_media = (yield from $this->getDownloadInfo($message_media));
|
||||
StatCache::clear($file);
|
||||
|
||||
$size = (yield \stat($file))['size'];
|
||||
$stream = yield open($file, 'cb');
|
||||
|
||||
$this->logger->logger('Waiting for lock of file to download...');
|
||||
$unlock = yield \danog\MadelineProto\Tools::flock($file, LOCK_EX);
|
||||
$this->logger->logger('Got lock of file to download');
|
||||
|
||||
try {
|
||||
yield $this->downloadToStream($message_media, $stream, $cb, $size, -1);
|
||||
yield from $this->downloadToStream($message_media, $stream, $cb, $size, -1);
|
||||
} finally {
|
||||
$unlock();
|
||||
yield $stream->close();
|
||||
StatCache::clear($file);
|
||||
}
|
||||
|
||||
return $file;
|
||||
}
|
||||
/**
|
||||
@ -1112,13 +966,11 @@ trait Files
|
||||
*/
|
||||
public function downloadToStream($message_media, $stream, $cb = null, int $offset = 0, int $end = -1): \Generator
|
||||
{
|
||||
$message_media = yield $this->getDownloadInfo($message_media);
|
||||
|
||||
$message_media = (yield from $this->getDownloadInfo($message_media));
|
||||
if (\is_object($stream) && $stream instanceof FileCallbackInterface) {
|
||||
$cb = $stream;
|
||||
$stream = $stream->getFile();
|
||||
}
|
||||
|
||||
/** @var $stream \Amp\ByteStream\OutputStream */
|
||||
if (!\is_object($stream)) {
|
||||
$stream = new ResourceOutputStream($stream);
|
||||
@ -1134,7 +986,7 @@ trait Files
|
||||
} catch (StreamException $e) {
|
||||
}
|
||||
}
|
||||
$callable = static function (string $payload, int $offset) use ($stream, $seekable) {
|
||||
$callable = static function (string $payload, int $offset) use ($stream, $seekable): \Generator {
|
||||
if ($seekable) {
|
||||
while ($stream->tell() !== $offset) {
|
||||
yield $stream->seek($offset);
|
||||
@ -1142,8 +994,7 @@ trait Files
|
||||
}
|
||||
return yield $stream->write($payload);
|
||||
};
|
||||
|
||||
return yield $this->downloadToCallable($message_media, $callable, $cb, $seekable, $offset, $end);
|
||||
return yield from $this->downloadToCallable($message_media, $callable, $cb, $seekable, $offset, $end);
|
||||
}
|
||||
/**
|
||||
* Download file to callable.
|
||||
@ -1163,13 +1014,11 @@ trait Files
|
||||
*/
|
||||
public function downloadToCallable($message_media, $callable, $cb = null, bool $seekable = true, int $offset = 0, int $end = -1, int $part_size = null): \Generator
|
||||
{
|
||||
$message_media = yield $this->getDownloadInfo($message_media);
|
||||
|
||||
$message_media = (yield from $this->getDownloadInfo($message_media));
|
||||
if (\is_object($callable) && $callable instanceof FileCallbackInterface) {
|
||||
$cb = $callable;
|
||||
$callable = $callable->getFile();
|
||||
}
|
||||
|
||||
if (!\is_callable($callable)) {
|
||||
throw new Exception('Wrong callable provided');
|
||||
}
|
||||
@ -1178,19 +1027,15 @@ trait Files
|
||||
$this->logger->logger('Download status: ' . $percent . '%', \danog\MadelineProto\Logger::NOTICE);
|
||||
};
|
||||
}
|
||||
|
||||
if ($end === -1 && isset($message_media['size'])) {
|
||||
$end = $message_media['size'];
|
||||
}
|
||||
|
||||
$part_size = $part_size ?? $this->settings['download']['part_size'];
|
||||
$parallel_chunks = $this->settings['download']['parallel_chunks'] ? $this->settings['download']['parallel_chunks'] : 3000;
|
||||
|
||||
$datacenter = isset($message_media['InputFileLocation']['dc_id']) ? $message_media['InputFileLocation']['dc_id'] : $this->settings['connection_settings']['default_dc'];
|
||||
if ($this->datacenter->has($datacenter . '_media')) {
|
||||
$datacenter .= '_media';
|
||||
}
|
||||
|
||||
if (isset($message_media['key'])) {
|
||||
$digest = \hash('md5', $message_media['key'] . $message_media['iv'], true);
|
||||
$fingerprint = \danog\MadelineProto\Tools::unpackSignedInt(\substr($digest, 0, 4) ^ \substr($digest, 4, 4));
|
||||
@ -1203,7 +1048,6 @@ trait Files
|
||||
$ige->enableContinuousBuffer();
|
||||
$seekable = false;
|
||||
}
|
||||
|
||||
if ($offset === $end) {
|
||||
$cb(100, 0, 0);
|
||||
return true;
|
||||
@ -1211,35 +1055,24 @@ trait Files
|
||||
$params = [];
|
||||
$start_at = $offset % $part_size;
|
||||
$probable_end = $end !== -1 ? $end : 512 * 1024 * 3000;
|
||||
|
||||
$breakOut = false;
|
||||
for ($x = $offset - $start_at; $x < $probable_end; $x += $part_size) {
|
||||
$end_at = $part_size;
|
||||
|
||||
if ($end !== -1 && $x + $part_size > $end) {
|
||||
$end_at = $end % $part_size;
|
||||
$breakOut = true;
|
||||
}
|
||||
|
||||
$params[] = [
|
||||
'offset' => $x,
|
||||
'limit' => $part_size,
|
||||
'part_start_at' => $start_at,
|
||||
'part_end_at' => $end_at,
|
||||
];
|
||||
|
||||
$params[] = ['offset' => $x, 'limit' => $part_size, 'part_start_at' => $start_at, 'part_end_at' => $end_at];
|
||||
$start_at = 0;
|
||||
if ($breakOut) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$params) {
|
||||
$cb(100, 0, 0);
|
||||
return true;
|
||||
}
|
||||
$count = \count($params);
|
||||
|
||||
$time = 0;
|
||||
$speed = 0;
|
||||
$origCb = $cb;
|
||||
@ -1248,21 +1081,17 @@ trait Files
|
||||
$cur++;
|
||||
\danog\MadelineProto\Tools::callFork($cb($cur * 100 / $count, $time, $speed));
|
||||
};
|
||||
|
||||
$cdn = false;
|
||||
|
||||
$params[0]['previous_promise'] = new Success(true);
|
||||
|
||||
$start = \microtime(true);
|
||||
$size = yield $this->downloadPart($message_media, $cdn, $datacenter, $old_dc, $ige, $cb, $initParam = \array_shift($params), $callable, $seekable);
|
||||
if ($initParam['part_end_at'] - $initParam['part_start_at'] !== $size) { // Premature end for undefined length files
|
||||
$size = (yield from $this->downloadPart($message_media, $cdn, $datacenter, $old_dc, $ige, $cb, $initParam = \array_shift($params), $callable, $seekable));
|
||||
if ($initParam['part_end_at'] - $initParam['part_start_at'] !== $size) {
|
||||
// Premature end for undefined length files
|
||||
$origCb(100, 0, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($params) {
|
||||
$previous_promise = new Success(true);
|
||||
|
||||
$promises = [];
|
||||
foreach ($params as $key => $param) {
|
||||
$param['previous_promise'] = $previous_promise;
|
||||
@ -1272,10 +1101,9 @@ trait Files
|
||||
$size += $res;
|
||||
}
|
||||
});
|
||||
|
||||
$promises[] = $previous_promise;
|
||||
|
||||
if (!($key % $parallel_chunks)) { // 20 mb at a time, for a typical bandwidth of 1gbps
|
||||
if (!($key % $parallel_chunks)) {
|
||||
// 20 mb at a time, for a typical bandwidth of 1gbps
|
||||
$res = yield \danog\MadelineProto\Tools::all($promises);
|
||||
$promises = [];
|
||||
foreach ($res as $r) {
|
||||
@ -1283,11 +1111,10 @@ trait Files
|
||||
break 2;
|
||||
}
|
||||
}
|
||||
|
||||
$time = \microtime(true) - $start;
|
||||
$speed = (int) (($size * 8) / $time) / 1000000;
|
||||
$this->logger->logger("Partial download time: $time");
|
||||
$this->logger->logger("Partial download speed: $speed mbps");
|
||||
$speed = (int) ($size * 8 / $time) / 1000000;
|
||||
$this->logger->logger("Partial download time: {$time}");
|
||||
$this->logger->logger("Partial download speed: {$speed} mbps");
|
||||
}
|
||||
}
|
||||
if ($promises) {
|
||||
@ -1295,21 +1122,17 @@ trait Files
|
||||
}
|
||||
}
|
||||
$time = \microtime(true) - $start;
|
||||
$speed = (int) (($size * 8) / $time) / 1000000;
|
||||
$this->logger->logger("Total download time: $time");
|
||||
$this->logger->logger("Total download speed: $speed mbps");
|
||||
|
||||
$speed = (int) ($size * 8 / $time) / 1000000;
|
||||
$this->logger->logger("Total download time: {$time}");
|
||||
$this->logger->logger("Total download speed: {$speed} mbps");
|
||||
if ($cdn) {
|
||||
$this->clearCdnHashes($message_media['file_token']);
|
||||
}
|
||||
|
||||
if (!isset($message_media['size'])) {
|
||||
$origCb(100, $time, $speed);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Download file part.
|
||||
*
|
||||
@ -1329,34 +1152,20 @@ trait Files
|
||||
private function downloadPart(&$message_media, bool &$cdn, &$datacenter, &$old_dc, &$ige, $cb, array $offset, $callable, bool $seekable, bool $postpone = false): \Generator
|
||||
{
|
||||
static $method = [
|
||||
false => 'upload.getFile', // non-cdn
|
||||
true => 'upload.getCdnFile', // cdn
|
||||
false => 'upload.getFile',
|
||||
// non-cdn
|
||||
true => 'upload.getCdnFile',
|
||||
];
|
||||
do {
|
||||
if (!$cdn) {
|
||||
$basic_param = [
|
||||
'location' => $message_media['InputFileLocation'],
|
||||
];
|
||||
$basic_param = ['location' => $message_media['InputFileLocation']];
|
||||
} else {
|
||||
$basic_param = [
|
||||
'file_token' => $message_media['file_token'],
|
||||
];
|
||||
$basic_param = ['file_token' => $message_media['file_token']];
|
||||
}
|
||||
|
||||
//$x = 0;
|
||||
while (true) {
|
||||
try {
|
||||
$res = yield $this->methodCallAsyncRead(
|
||||
$method[$cdn],
|
||||
$basic_param + $offset,
|
||||
[
|
||||
'heavy' => true,
|
||||
'file' => true,
|
||||
'FloodWaitLimit' => 0,
|
||||
'datacenter' => &$datacenter,
|
||||
'postpone' => $postpone,
|
||||
]
|
||||
);
|
||||
$res = yield $this->methodCallAsyncRead($method[$cdn], $basic_param + $offset, ['heavy' => true, 'file' => true, 'FloodWaitLimit' => 0, 'datacenter' => &$datacenter, 'postpone' => $postpone]);
|
||||
break;
|
||||
} catch (\danog\MadelineProto\RPCErrorException $e) {
|
||||
if (\strpos($e->rpc, 'FLOOD_WAIT_') === 0) {
|
||||
@ -1372,7 +1181,6 @@ trait Files
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($res['_'] === 'upload.fileCdnRedirect') {
|
||||
$cdn = true;
|
||||
$message_media['file_token'] = $res['file_token'];
|
||||
@ -1388,7 +1196,6 @@ trait Files
|
||||
} elseif ($res['_'] === 'upload.cdnFileReuploadNeeded') {
|
||||
$this->logger->logger(\danog\MadelineProto\Lang::$current_lang['cdn_reupload'], \danog\MadelineProto\Logger::NOTICE);
|
||||
yield $this->getConfig([], ['datacenter' => $this->datacenter->curdc]);
|
||||
|
||||
try {
|
||||
$this->addCdnHashes($message_media['file_token'], yield $this->methodCallAsyncRead('upload.reuploadCdnFile', ['file_token' => $message_media['file_token'], 'request_token' => $res['request_token']], ['heavy' => true, 'datacenter' => $old_dc]));
|
||||
} catch (\danog\MadelineProto\RPCErrorException $e) {
|
||||
@ -1404,22 +1211,15 @@ trait Files
|
||||
continue;
|
||||
}
|
||||
$res['bytes'] = (string) $res['bytes'];
|
||||
|
||||
if ($cdn === false && $res['type']['_'] === 'storage.fileUnknown' && $res['bytes'] === '') {
|
||||
$datacenter = 0;
|
||||
}
|
||||
while ($cdn === false &&
|
||||
$res['type']['_'] === 'storage.fileUnknown' &&
|
||||
$res['bytes'] === '' &&
|
||||
$this->datacenter->has(++$datacenter)
|
||||
) {
|
||||
while ($cdn === false && $res['type']['_'] === 'storage.fileUnknown' && $res['bytes'] === '' && $this->datacenter->has(++$datacenter)) {
|
||||
$res = yield $this->methodCallAsyncRead('upload.getFile', $basic_param + $offset, ['heavy' => true, 'file' => true, 'FloodWaitLimit' => 0, 'datacenter' => $datacenter]);
|
||||
}
|
||||
|
||||
if ($res['bytes'] === '') {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (isset($message_media['cdn_key'])) {
|
||||
$ivec = \substr($message_media['cdn_iv'], 0, 12) . \pack('N', $offset['offset'] >> 4);
|
||||
$res['bytes'] = $this->ctrEncrypt($res['bytes'], $message_media['cdn_key'], $ivec);
|
||||
@ -1431,18 +1231,15 @@ trait Files
|
||||
if ($offset['part_start_at'] || $offset['part_end_at'] !== $offset['limit']) {
|
||||
$res['bytes'] = \substr($res['bytes'], $offset['part_start_at'], $offset['part_end_at'] - $offset['part_start_at']);
|
||||
}
|
||||
|
||||
if (!$seekable) {
|
||||
yield $offset['previous_promise'];
|
||||
yield from $offset['previous_promise'];
|
||||
}
|
||||
$res = yield $callable($res['bytes'], $offset['offset'] + $offset['part_start_at']);
|
||||
$cb();
|
||||
return $res;
|
||||
} while (true);
|
||||
}
|
||||
|
||||
private $cdn_hashes = [];
|
||||
|
||||
private function addCdnHashes($file, $hashes)
|
||||
{
|
||||
if (!isset($this->cdn_hashes[$file])) {
|
||||
@ -1452,8 +1249,7 @@ trait Files
|
||||
$this->cdn_hashes[$file][$hash['offset']] = ['limit' => $hash['limit'], 'hash' => (string) $hash['hash']];
|
||||
}
|
||||
}
|
||||
|
||||
private function checkCdnHash($file, $offset, $data, &$datacenter)
|
||||
private function checkCdnHash($file, $offset, $data, &$datacenter): \Generator
|
||||
{
|
||||
while (\strlen($data)) {
|
||||
if (!isset($this->cdn_hashes[$file][$offset])) {
|
||||
@ -1468,14 +1264,11 @@ trait Files
|
||||
$data = \substr($data, $this->cdn_hashes[$file][$offset]['limit']);
|
||||
$offset += $this->cdn_hashes[$file][$offset]['limit'];
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function clearCdnHashes($file)
|
||||
{
|
||||
unset($this->cdn_hashes[$file]);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -29,26 +29,8 @@ use danog\MadelineProto\Tools;
|
||||
class MinDatabase implements TLCallback
|
||||
{
|
||||
use Tools;
|
||||
|
||||
const SWITCH_CONSTRUCTORS = [
|
||||
'inputChannel',
|
||||
'inputUser',
|
||||
'inputPeerUser',
|
||||
'inputPeerChannel',
|
||||
];
|
||||
const CATCH_PEERS = [
|
||||
'message',
|
||||
'messageService',
|
||||
'peerUser',
|
||||
'peerChannel',
|
||||
'messageEntityMentionName',
|
||||
|
||||
'messageFwdHeader',
|
||||
'messageActionChatCreate',
|
||||
'messageActionChatAddUser',
|
||||
'messageActionChatDeleteUser',
|
||||
'messageActionChatJoinedByLink',
|
||||
];
|
||||
const SWITCH_CONSTRUCTORS = ['inputChannel', 'inputUser', 'inputPeerUser', 'inputPeerChannel'];
|
||||
const CATCH_PEERS = ['message', 'messageService', 'peerUser', 'peerChannel', 'messageEntityMentionName', 'messageFwdHeader', 'messageActionChatCreate', 'messageActionChatAddUser', 'messageActionChatDeleteUser', 'messageActionChatJoinedByLink'];
|
||||
const ORIGINS = ['message', 'messageService'];
|
||||
/**
|
||||
* References indexed by location.
|
||||
@ -68,23 +50,19 @@ class MinDatabase implements TLCallback
|
||||
* @var \danog\MadelineProto\MTProto
|
||||
*/
|
||||
private $API;
|
||||
|
||||
public function __construct(MTProto $API)
|
||||
{
|
||||
$this->API = $API;
|
||||
$this->init();
|
||||
}
|
||||
|
||||
public function __wakeup()
|
||||
{
|
||||
$this->init();
|
||||
}
|
||||
|
||||
public function __sleep()
|
||||
{
|
||||
return ['db', 'API'];
|
||||
}
|
||||
|
||||
public function init()
|
||||
{
|
||||
foreach ($this->db as $id => $origin) {
|
||||
@ -93,40 +71,30 @@ class MinDatabase implements TLCallback
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getMethodCallbacks(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function getMethodBeforeCallbacks(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function getConstructorCallbacks(): array
|
||||
{
|
||||
return \array_merge(
|
||||
\array_fill_keys(self::CATCH_PEERS, [[$this, 'addPeer']]),
|
||||
\array_fill_keys(self::ORIGINS, [[$this, 'addOrigin']])
|
||||
);
|
||||
return \array_merge(\array_fill_keys(self::CATCH_PEERS, [[$this, 'addPeer']]), \array_fill_keys(self::ORIGINS, [[$this, 'addOrigin']]));
|
||||
}
|
||||
|
||||
public function getConstructorBeforeCallbacks(): array
|
||||
{
|
||||
return \array_fill_keys(self::ORIGINS, [[$this, 'addOriginContext']]);
|
||||
}
|
||||
|
||||
public function getConstructorSerializeCallbacks(): array
|
||||
{
|
||||
return \array_fill_keys(self::SWITCH_CONSTRUCTORS, [$this, 'populateFrom']);
|
||||
}
|
||||
|
||||
public function getTypeMismatchCallbacks(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function reset()
|
||||
{
|
||||
if ($this->cache) {
|
||||
@ -134,7 +102,6 @@ class MinDatabase implements TLCallback
|
||||
$this->cache = [];
|
||||
}
|
||||
}
|
||||
|
||||
public function addPeer(array $location)
|
||||
{
|
||||
if (!$this->cache) {
|
||||
@ -148,18 +115,15 @@ class MinDatabase implements TLCallback
|
||||
if ($frame['args'][1]['subtype'] === $previous) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$frames[] = $frame['args'][1]['subtype'];
|
||||
$previous = $frame['args'][1]['subtype'];
|
||||
} elseif (isset($frame['args'][1]['type'])) {
|
||||
if ($frame['args'][1]['type'] === '') {
|
||||
break;
|
||||
}
|
||||
|
||||
if ($frame['args'][1]['type'] === $previous) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$frames[] = $frame['args'][1]['type'];
|
||||
$previous = $frame['args'][1]['type'];
|
||||
}
|
||||
@ -171,7 +135,6 @@ class MinDatabase implements TLCallback
|
||||
$tl_trace .= "['" . $frame . "']";
|
||||
}
|
||||
$this->API->logger->logger($tl_trace, \danog\MadelineProto\Logger::ERROR);
|
||||
|
||||
return false;
|
||||
}
|
||||
$peers = [];
|
||||
@ -204,16 +167,13 @@ class MinDatabase implements TLCallback
|
||||
foreach ($peers as $id => $true) {
|
||||
$this->cache[$key][$id] = $id;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function addOriginContext(string $type)
|
||||
{
|
||||
$this->API->logger->logger("Adding peer origin context for {$type}!", \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
$this->cache[] = [];
|
||||
}
|
||||
|
||||
public function addOrigin(array $data = [])
|
||||
{
|
||||
$cache = \array_pop($this->cache);
|
||||
@ -238,8 +198,7 @@ class MinDatabase implements TLCallback
|
||||
}
|
||||
$this->API->logger->logger("Added origin ({$data['_']}) to " . \count($cache) . ' peer locations', \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
}
|
||||
|
||||
public function populateFrom(array $object)
|
||||
public function populateFrom(array $object): \Generator
|
||||
{
|
||||
if (!($object['min'] ?? false)) {
|
||||
return $object;
|
||||
@ -250,17 +209,14 @@ class MinDatabase implements TLCallback
|
||||
$new['_'] .= 'FromMessage';
|
||||
$new['peer'] = (yield $this->API->getInfo($new['peer']))['InputPeer'];
|
||||
if ($new['peer']['min']) {
|
||||
$this->API->logger->logger("Don't have origin peer subinfo with min peer $id, this may fail");
|
||||
$this->API->logger->logger("Don't have origin peer subinfo with min peer {$id}, this may fail");
|
||||
return $object;
|
||||
}
|
||||
return $new;
|
||||
}
|
||||
$this->API->logger->logger("Don't have origin info with min peer $id, this may fail");
|
||||
|
||||
|
||||
$this->API->logger->logger("Don't have origin info with min peer {$id}, this may fail");
|
||||
return $object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if location info is available for peer.
|
||||
*
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Password calculator module.
|
||||
*
|
||||
@ -34,7 +35,6 @@ class PasswordCalculator
|
||||
{
|
||||
use AuthKeyHandler;
|
||||
use Tools;
|
||||
|
||||
/**
|
||||
* The algorithm to use for calculating the hash of new passwords (a PasswordKdfAlgo object).
|
||||
*
|
||||
@ -47,14 +47,12 @@ class PasswordCalculator
|
||||
* @var string
|
||||
*/
|
||||
private $secure_random = '';
|
||||
|
||||
/**
|
||||
* The algorithm to use for calculatuing the hash of the current password (a PasswordKdfAlgo object).
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $current_algo;
|
||||
|
||||
/**
|
||||
* SRP b parameter.
|
||||
*
|
||||
@ -79,7 +77,6 @@ class PasswordCalculator
|
||||
* @var \danog\MadelineProto\Logger
|
||||
*/
|
||||
public $logger;
|
||||
|
||||
/**
|
||||
* Initialize logger.
|
||||
*
|
||||
@ -89,7 +86,6 @@ class PasswordCalculator
|
||||
{
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Popupate 2FA configuration.
|
||||
*
|
||||
@ -115,7 +111,6 @@ class PasswordCalculator
|
||||
$this->checkPG($object['current_algo']['p'], $object['current_algo']['g']);
|
||||
$object['current_algo']['gForHash'] = \str_pad($object['current_algo']['g']->toBytes(), 256, \chr(0), \STR_PAD_LEFT);
|
||||
$object['current_algo']['pForHash'] = \str_pad($object['current_algo']['p']->toBytes(), 256, \chr(0), \STR_PAD_LEFT);
|
||||
|
||||
break;
|
||||
default:
|
||||
throw new Exception("Unknown KDF algo {$object['current_algo']['_']}");
|
||||
@ -146,7 +141,6 @@ class PasswordCalculator
|
||||
$this->checkPG($object['new_algo']['p'], $object['new_algo']['g']);
|
||||
$object['new_algo']['gForHash'] = \str_pad($object['new_algo']['g']->toBytes(), 256, \chr(0), \STR_PAD_LEFT);
|
||||
$object['new_algo']['pForHash'] = \str_pad($object['new_algo']['p']->toBytes(), 256, \chr(0), \STR_PAD_LEFT);
|
||||
|
||||
break;
|
||||
default:
|
||||
throw new Exception("Unknown KDF algo {$object['new_algo']['_']}");
|
||||
@ -154,7 +148,6 @@ class PasswordCalculator
|
||||
$this->new_algo = $object['new_algo'];
|
||||
$this->secure_random = $object['secure_random'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a random string (eventually prefixed by the specified string).
|
||||
*
|
||||
@ -165,7 +158,6 @@ class PasswordCalculator
|
||||
{
|
||||
return $prefix . \danog\MadelineProto\Tools::random(32);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hash specified data using the salt with SHA256.
|
||||
*
|
||||
@ -179,7 +171,6 @@ class PasswordCalculator
|
||||
{
|
||||
return \hash('sha256', $salt . $data . $salt, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hashes the specified password.
|
||||
*
|
||||
@ -193,10 +184,8 @@ class PasswordCalculator
|
||||
$buf = $this->hashSha256($password, $client_salt);
|
||||
$buf = $this->hashSha256($buf, $server_salt);
|
||||
$hash = \hash_pbkdf2('sha512', $buf, $client_salt, 100000, 0, true);
|
||||
|
||||
return $this->hashSha256($hash, $server_salt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the InputCheckPassword object for checking the validity of a password using account.checkPassword.
|
||||
*
|
||||
@ -214,42 +203,30 @@ class PasswordCalculator
|
||||
$gForHash = $this->current_algo['gForHash'];
|
||||
$p = $this->current_algo['p'];
|
||||
$pForHash = $this->current_algo['pForHash'];
|
||||
|
||||
$B = $this->srp_B;
|
||||
$BForHash = $this->srp_BForHash;
|
||||
$id = $this->srp_id;
|
||||
|
||||
$x = new BigInteger($this->hashPassword($password, $client_salt, $server_salt), 256);
|
||||
$g_x = $g->powMod($x, $p);
|
||||
|
||||
$k = new BigInteger(\hash('sha256', $pForHash . $gForHash, true), 256);
|
||||
$kg_x = $k->multiply($g_x)->powMod(Magic::$one, $p);
|
||||
|
||||
$a = new BigInteger(\danog\MadelineProto\Tools::random(2048 / 8), 256);
|
||||
$A = $g->powMod($a, $p);
|
||||
$this->checkG($A, $p);
|
||||
$AForHash = \str_pad($A->toBytes(), 256, \chr(0), \STR_PAD_LEFT);
|
||||
|
||||
$b_kg_x = $B->powMod(Magic::$one, $p)->subtract($kg_x);
|
||||
|
||||
$u = new BigInteger(\hash('sha256', $AForHash . $BForHash, true), 256);
|
||||
$ux = $u->multiply($x);
|
||||
$a_ux = $a->add($ux);
|
||||
|
||||
$S = $b_kg_x->powMod($a_ux, $p);
|
||||
|
||||
$SForHash = \str_pad($S->toBytes(), 256, \chr(0), \STR_PAD_LEFT);
|
||||
$K = \hash('sha256', $SForHash, true);
|
||||
|
||||
$h1 = \hash('sha256', $pForHash, true);
|
||||
$h2 = \hash('sha256', $gForHash, true);
|
||||
$h1 ^= $h2;
|
||||
|
||||
$M1 = \hash('sha256', $h1 . \hash('sha256', $client_salt, true) . \hash('sha256', $server_salt, true) . $AForHash . $BForHash . $K, true);
|
||||
|
||||
return ['_' => 'inputCheckPasswordSRP', 'srp_id' => $id, 'A' => $AForHash, 'M1' => $M1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get parameters to be passed to the account.updatePasswordSettings to update/set a 2FA password.
|
||||
*
|
||||
@ -261,36 +238,24 @@ class PasswordCalculator
|
||||
public function getPassword(array $params): array
|
||||
{
|
||||
$oldPassword = $this->getCheckPassword($params['password'] ?? '');
|
||||
|
||||
$return = ['password' => $oldPassword, 'new_settings' => ['_' => 'account.passwordInputSettings', 'new_algo' => ['_' => 'passwordKdfAlgoUnknown'], 'new_password_hash' => '', 'hint' => '']];
|
||||
|
||||
$new_settings =& $return['new_settings'];
|
||||
|
||||
if (isset($params['new_password']) && $params['new_password'] !== '') {
|
||||
$client_salt = $this->createSalt($this->new_algo['salt1']);
|
||||
$server_salt = $this->new_algo['salt2'];
|
||||
$g = $this->new_algo['g'];
|
||||
$p = $this->new_algo['p'];
|
||||
$pForHash = $this->new_algo['pForHash'];
|
||||
|
||||
$x = new BigInteger($this->hashPassword($params['new_password'], $client_salt, $server_salt), 256);
|
||||
$v = $g->powMod($x, $p);
|
||||
$vForHash = \str_pad($v->toBytes(), 256, \chr(0), \STR_PAD_LEFT);
|
||||
|
||||
$new_settings['new_algo'] = [
|
||||
'_' => 'passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow',
|
||||
'salt1' => $client_salt,
|
||||
'salt2' => $server_salt,
|
||||
'g' => (int) $g->toString(),
|
||||
'p' => $pForHash,
|
||||
];
|
||||
$new_settings['new_algo'] = ['_' => 'passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow', 'salt1' => $client_salt, 'salt2' => $server_salt, 'g' => (int) $g->toString(), 'p' => $pForHash];
|
||||
$new_settings['new_password_hash'] = $vForHash;
|
||||
$new_settings['hint'] = $params['hint'] ?? '';
|
||||
if (isset($params['email'])) {
|
||||
$new_settings['email'] = $params['email'];
|
||||
}
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
}
|
||||
|
@ -29,9 +29,7 @@ trait PeerHandler
|
||||
public $caching_simple = [];
|
||||
public $caching_simple_username = [];
|
||||
public $caching_possible_username = [];
|
||||
|
||||
public $caching_full_info = [];
|
||||
|
||||
/**
|
||||
* Convert MTProto channel ID to bot API channel ID.
|
||||
*
|
||||
@ -43,7 +41,6 @@ trait PeerHandler
|
||||
{
|
||||
return -($id + \pow(10, (int) \floor(\log($id, 10) + 3)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert bot API channel ID to MTProto channel ID.
|
||||
*
|
||||
@ -55,7 +52,6 @@ trait PeerHandler
|
||||
{
|
||||
return -$id - \pow(10, (int) \floor(\log(-$id, 10)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether provided bot API ID is a channel.
|
||||
*
|
||||
@ -66,10 +62,8 @@ trait PeerHandler
|
||||
public static function isSupergroup($id): bool
|
||||
{
|
||||
$log = \log(-$id, 10);
|
||||
|
||||
return ($log - \intval($log)) * 1000 < 10;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set support info.
|
||||
*
|
||||
@ -83,7 +77,6 @@ trait PeerHandler
|
||||
{
|
||||
$this->supportUser = $support['user']['id'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Add user info.
|
||||
*
|
||||
@ -109,14 +102,12 @@ trait PeerHandler
|
||||
} else {
|
||||
$this->logger->logger("No access hash with user {$user['id']}, tried and failed to fetch data...");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
switch ($user['_']) {
|
||||
case 'user':
|
||||
if (!isset($this->chats[$user['id']]) || $this->chats[$user['id']] !== $user) {
|
||||
$this->logger->logger("Updated user {$user['id']}", \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
|
||||
if (($user['min'] ?? false) && isset($this->chats[$user['id']]) && !($this->chats[$user['id']]['min'] ?? false)) {
|
||||
$this->logger->logger("{$user['id']} is min, filling missing fields", \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
if (isset($this->chats[$user['id']]['access_hash'])) {
|
||||
@ -124,7 +115,6 @@ trait PeerHandler
|
||||
$user['access_hash'] = $this->chats[$user['id']]['access_hash'];
|
||||
}
|
||||
}
|
||||
|
||||
$this->chats[$user['id']] = $user;
|
||||
$this->cachePwrChat($user['id'], false, true);
|
||||
}
|
||||
@ -135,7 +125,6 @@ trait PeerHandler
|
||||
throw new \danog\MadelineProto\Exception('Invalid user provided', $user);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add chat to database.
|
||||
*
|
||||
@ -168,7 +157,6 @@ trait PeerHandler
|
||||
if (isset($chat['username']) && !isset($this->caching_simple_username[$chat['username']])) {
|
||||
$this->caching_possible_username[$bot_api_id] = $chat['username'];
|
||||
}
|
||||
|
||||
$this->cachePwrChat($bot_api_id, false, true);
|
||||
} elseif (isset($chat['username']) && !isset($this->chats[$bot_api_id]) && !isset($this->caching_simple_username[$chat['username']])) {
|
||||
$this->logger->logger("No access hash with {$chat['_']} {$bot_api_id}, trying to fetch by username...");
|
||||
@ -176,14 +164,12 @@ trait PeerHandler
|
||||
} else {
|
||||
$this->logger->logger("No access hash with {$chat['_']} {$bot_api_id}, tried and failed to fetch data...");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
if (!isset($this->chats[$bot_api_id]) || $this->chats[$bot_api_id] !== $chat) {
|
||||
$this->logger->logger("Updated chat $bot_api_id", \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
|
||||
$this->logger->logger("Updated chat {$bot_api_id}", \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
if (($chat['min'] ?? false) && isset($this->chats[$bot_api_id]) && !($this->chats[$bot_api_id]['min'] ?? false)) {
|
||||
$this->logger->logger("$bot_api_id is min, filling missing fields", \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
$this->logger->logger("{$bot_api_id} is min, filling missing fields", \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
$newchat = $this->chats[$bot_api_id];
|
||||
foreach (['title', 'username', 'photo', 'banned_rights', 'megagroup', 'verified'] as $field) {
|
||||
if (isset($chat[$field])) {
|
||||
@ -192,10 +178,8 @@ trait PeerHandler
|
||||
}
|
||||
$chat = $newchat;
|
||||
}
|
||||
|
||||
$this->chats[$bot_api_id] = $chat;
|
||||
|
||||
if ($this->settings['peer']['full_fetch'] && (!isset($this->full_chats[$bot_api_id]) || $this->full_chats[$bot_api_id]['full']['participants_count'] !== (yield $this->getFullInfo($bot_api_id))['full']['participants_count'])) {
|
||||
if ($this->settings['peer']['full_fetch'] && (!isset($this->full_chats[$bot_api_id]) || $this->full_chats[$bot_api_id]['full']['participants_count'] !== (yield from $this->getFullInfo($bot_api_id))['full']['participants_count'])) {
|
||||
$this->cachePwrChat($bot_api_id, $this->settings['peer']['full_fetch'], true);
|
||||
}
|
||||
}
|
||||
@ -205,12 +189,11 @@ trait PeerHandler
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private function cachePwrChat($id, $full_fetch, $send)
|
||||
{
|
||||
\danog\MadelineProto\Tools::callFork((function () use ($id, $full_fetch, $send) {
|
||||
\danog\MadelineProto\Tools::callFork((function () use ($id, $full_fetch, $send): \Generator {
|
||||
try {
|
||||
yield $this->getPwrChat($id, $full_fetch, $send);
|
||||
yield from $this->getPwrChat($id, $full_fetch, $send);
|
||||
} catch (\danog\MadelineProto\Exception $e) {
|
||||
$this->logger->logger('While caching: ' . $e->getMessage(), \danog\MadelineProto\Logger::WARNING);
|
||||
} catch (\danog\MadelineProto\RPCErrorException $e) {
|
||||
@ -218,7 +201,6 @@ trait PeerHandler
|
||||
}
|
||||
})());
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if peer is present in internal peer database.
|
||||
*
|
||||
@ -229,7 +211,7 @@ trait PeerHandler
|
||||
public function peerIsset($id): \Generator
|
||||
{
|
||||
try {
|
||||
return isset($this->chats[(yield $this->getInfo($id))['bot_api_id']]);
|
||||
return isset($this->chats[(yield from $this->getInfo($id))['bot_api_id']]);
|
||||
} catch (\danog\MadelineProto\Exception $e) {
|
||||
return false;
|
||||
} catch (\danog\MadelineProto\RPCErrorException $e) {
|
||||
@ -239,11 +221,9 @@ trait PeerHandler
|
||||
if ($e->rpc === 'CHANNEL_PRIVATE') {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if all peer entities are in db.
|
||||
*
|
||||
@ -258,7 +238,7 @@ trait PeerHandler
|
||||
try {
|
||||
foreach ($entities as $entity) {
|
||||
if ($entity['_'] === 'messageEntityMentionName' || $entity['_'] === 'inputMessageEntityMentionName') {
|
||||
if (!yield $this->peerIsset($entity['user_id'])) {
|
||||
if (!(yield from $this->peerIsset($entity['user_id']))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -266,10 +246,8 @@ trait PeerHandler
|
||||
} catch (\danog\MadelineProto\Exception $e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if fwd peer is set.
|
||||
*
|
||||
@ -282,19 +260,17 @@ trait PeerHandler
|
||||
public function fwdPeerIsset(array $fwd): \Generator
|
||||
{
|
||||
try {
|
||||
if (isset($fwd['user_id']) && !yield $this->peerIsset($fwd['user_id'])) {
|
||||
if (isset($fwd['user_id']) && !(yield from $this->peerIsset($fwd['user_id']))) {
|
||||
return false;
|
||||
}
|
||||
if (isset($fwd['channel_id']) && !yield $this->peerIsset($this->toSupergroup($fwd['channel_id']))) {
|
||||
if (isset($fwd['channel_id']) && !(yield from $this->peerIsset($this->toSupergroup($fwd['channel_id'])))) {
|
||||
return false;
|
||||
}
|
||||
} catch (\danog\MadelineProto\Exception $e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get folder ID from object.
|
||||
*
|
||||
@ -339,15 +315,12 @@ trait PeerHandler
|
||||
case 'folderPeer':
|
||||
case 'inputFolderPeer':
|
||||
return $this->getId($id['peer']);
|
||||
|
||||
case 'inputUserFromMessage':
|
||||
case 'inputPeerUserFromMessage':
|
||||
return $id['user_id'];
|
||||
|
||||
case 'inputChannelFromMessage':
|
||||
case 'inputPeerChannelFromMessage':
|
||||
return $this->toSupergroup($id['channel_id']);
|
||||
|
||||
case 'inputUserSelf':
|
||||
case 'inputPeerSelf':
|
||||
return $this->authorization['user']['id'];
|
||||
@ -383,9 +356,7 @@ trait PeerHandler
|
||||
if (!isset($id['from_id']) || $id['to_id']['_'] !== 'peerUser' || $id['to_id']['user_id'] !== $this->authorization['user']['id']) {
|
||||
return $this->getId($id['to_id']);
|
||||
}
|
||||
|
||||
return $id['from_id'];
|
||||
|
||||
case 'updateChannelReadMessagesContents':
|
||||
case 'updateChannelAvailableMessages':
|
||||
case 'updateChannel':
|
||||
@ -439,13 +410,13 @@ trait PeerHandler
|
||||
}
|
||||
if (\is_string($id)) {
|
||||
if (\strpos($id, '#') !== false) {
|
||||
if (\preg_match('/^channel#(\d*)/', $id, $matches)) {
|
||||
if (\preg_match('/^channel#(\\d*)/', $id, $matches)) {
|
||||
return $this->toSupergroup($matches[1]);
|
||||
}
|
||||
if (\preg_match('/^chat#(\d*)/', $id, $matches)) {
|
||||
if (\preg_match('/^chat#(\\d*)/', $id, $matches)) {
|
||||
$id = '-' . $matches[1];
|
||||
}
|
||||
if (\preg_match('/^user#(\d*)/', $id, $matches)) {
|
||||
if (\preg_match('/^user#(\\d*)/', $id, $matches)) {
|
||||
return $matches[1];
|
||||
}
|
||||
}
|
||||
@ -454,13 +425,10 @@ trait PeerHandler
|
||||
if (\is_string($id)) {
|
||||
$id = \danog\MadelineProto\Magic::$bigint ? (float) $id : (int) $id;
|
||||
}
|
||||
|
||||
return $id;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get info about peer, returns an Info object.
|
||||
*
|
||||
@ -490,7 +458,6 @@ trait PeerHandler
|
||||
if (!isset($this->secret_chats[$id])) {
|
||||
throw new \danog\MadelineProto\Exception(\danog\MadelineProto\Lang::$current_lang['sec_peer_not_in_db']);
|
||||
}
|
||||
|
||||
return $this->secret_chats[$id];
|
||||
}
|
||||
}
|
||||
@ -499,13 +466,11 @@ trait PeerHandler
|
||||
if ($try_id !== false) {
|
||||
$id = $try_id;
|
||||
}
|
||||
|
||||
$tried_simple = false;
|
||||
|
||||
if (\is_numeric($id)) {
|
||||
if (!isset($this->chats[$id])) {
|
||||
try {
|
||||
$this->logger->logger("Try fetching $id with access hash 0");
|
||||
$this->logger->logger("Try fetching {$id} with access hash 0");
|
||||
$this->caching_simple[$id] = true;
|
||||
if ($id < 0) {
|
||||
if ($this->isSupergroup($id)) {
|
||||
@ -530,7 +495,7 @@ trait PeerHandler
|
||||
if (isset($this->chats[$id])) {
|
||||
if (($this->chats[$id]['min'] ?? false) && $this->minDatabase->hasPeer($id) && !isset($this->caching_full_info[$id])) {
|
||||
$this->caching_full_info[$id] = true;
|
||||
$this->logger->logger("Only have min peer for $id in database, trying to fetch full info");
|
||||
$this->logger->logger("Only have min peer for {$id} in database, trying to fetch full info");
|
||||
try {
|
||||
if ($id < 0) {
|
||||
yield $this->methodCallAsyncRead('channels.getChannels', ['id' => [$this->genAll($this->chats[$id], $folder_id)['InputChannel']]], ['datacenter' => $this->datacenter->curdc]);
|
||||
@ -545,7 +510,6 @@ trait PeerHandler
|
||||
unset($this->caching_full_info[$id]);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
return $this->genAll($this->chats[$id], $folder_id);
|
||||
} catch (\danog\MadelineProto\Exception $e) {
|
||||
@ -558,56 +522,50 @@ trait PeerHandler
|
||||
}
|
||||
if (!isset($this->settings['pwr']['requests']) || $this->settings['pwr']['requests'] === true && $recursive) {
|
||||
$dbres = [];
|
||||
|
||||
try {
|
||||
$dbres = \json_decode(yield $this->datacenter->fileGetContents('https://id.pwrtelegram.xyz/db/getusername?id=' . $id), true);
|
||||
} catch (\Throwable $e) {
|
||||
$this->logger->logger($e);
|
||||
}
|
||||
if (isset($dbres['ok']) && $dbres['ok']) {
|
||||
yield $this->resolveUsername('@'.$dbres['result']);
|
||||
|
||||
return yield $this->getInfo($id, false);
|
||||
yield from $this->resolveUsername('@' . $dbres['result']);
|
||||
return yield from $this->getInfo($id, false);
|
||||
}
|
||||
}
|
||||
if ($tried_simple && isset($this->caching_possible_username[$id])) {
|
||||
$this->logger->logger("No access hash with $id, trying to fetch by username...");
|
||||
|
||||
$this->logger->logger("No access hash with {$id}, trying to fetch by username...");
|
||||
$user = $this->caching_possible_username[$id];
|
||||
unset($this->caching_possible_username[$id]);
|
||||
|
||||
return yield $this->getInfo($user);
|
||||
return yield from $this->getInfo($user);
|
||||
}
|
||||
|
||||
throw new \danog\MadelineProto\Exception('This peer is not present in the internal peer database');
|
||||
}
|
||||
if (\preg_match('@(?:t|telegram)\.(?:me|dog)/(joinchat/)?([a-z0-9_-]*)@i', $id, $matches)) {
|
||||
if (\preg_match('@(?:t|telegram)\\.(?:me|dog)/(joinchat/)?([a-z0-9_-]*)@i', $id, $matches)) {
|
||||
if ($matches[1] === '') {
|
||||
$id = $matches[2];
|
||||
} else {
|
||||
$invite = yield $this->methodCallAsyncRead('messages.checkChatInvite', ['hash' => $matches[2]], ['datacenter' => $this->datacenter->curdc]);
|
||||
if (isset($invite['chat'])) {
|
||||
return yield $this->getInfo($invite['chat']);
|
||||
return yield from $this->getInfo($invite['chat']);
|
||||
}
|
||||
throw new \danog\MadelineProto\Exception('You have not joined this chat');
|
||||
}
|
||||
}
|
||||
$id = \strtolower(\str_replace('@', '', $id));
|
||||
if ($id === 'me') {
|
||||
return yield $this->getInfo($this->authorization['user']['id']);
|
||||
return yield from $this->getInfo($this->authorization['user']['id']);
|
||||
}
|
||||
if ($id === 'support') {
|
||||
if (!$this->supportUser) {
|
||||
yield $this->methodCallAsyncRead('help.getSupport', [], ['datacenter' => $this->settings['connection_settings']['default_dc']]);
|
||||
}
|
||||
|
||||
return yield $this->getInfo($this->supportUser);
|
||||
return yield from $this->getInfo($this->supportUser);
|
||||
}
|
||||
foreach ($this->chats as $bot_api_id => $chat) {
|
||||
if (isset($chat['username']) && \strtolower($chat['username']) === $id) {
|
||||
if ($chat['min'] ?? false && !isset($this->caching_full_info[$bot_api_id])) {
|
||||
$this->caching_full_info[$bot_api_id] = true;
|
||||
$this->logger->logger("Only have min peer for $bot_api_id in database, trying to fetch full info");
|
||||
$this->logger->logger("Only have min peer for {$bot_api_id} in database, trying to fetch full info");
|
||||
try {
|
||||
if ($bot_api_id < 0) {
|
||||
yield $this->methodCallAsyncRead('channels.getChannels', ['id' => [$this->genAll($this->chats[$bot_api_id], $folder_id)['InputChannel']]], ['datacenter' => $this->datacenter->curdc]);
|
||||
@ -622,19 +580,15 @@ trait PeerHandler
|
||||
unset($this->caching_full_info[$bot_api_id]);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->genAll($this->chats[$bot_api_id], $folder_id);
|
||||
}
|
||||
}
|
||||
if ($recursive) {
|
||||
yield $this->resolveUsername($id);
|
||||
|
||||
return yield $this->getInfo($id, false);
|
||||
yield from $this->resolveUsername($id);
|
||||
return yield from $this->getInfo($id, false);
|
||||
}
|
||||
|
||||
throw new \danog\MadelineProto\Exception('This peer is not present in the internal peer database');
|
||||
}
|
||||
|
||||
private function genAll($constructor, $folder_id = null)
|
||||
{
|
||||
$res = [$this->TL->getConstructors()->findByPredicate($constructor['_'])['type'] => $constructor];
|
||||
@ -656,7 +610,7 @@ trait PeerHandler
|
||||
$res['InputNotifyPeer'] = ['_' => 'inputNotifyPeer', 'peer' => $res['InputPeer']];
|
||||
$res['user_id'] = $constructor['id'];
|
||||
$res['bot_api_id'] = $constructor['id'];
|
||||
$res['type'] = ($constructor['bot'] ?? false) ? 'bot' : 'user';
|
||||
$res['type'] = $constructor['bot'] ?? false ? 'bot' : 'user';
|
||||
break;
|
||||
case 'chat':
|
||||
case 'chatForbidden':
|
||||
@ -683,7 +637,7 @@ trait PeerHandler
|
||||
$res['InputChannel'] = ['_' => 'inputChannel', 'channel_id' => $constructor['id'], 'access_hash' => $constructor['access_hash'], 'min' => $constructor['min']];
|
||||
$res['channel_id'] = $constructor['id'];
|
||||
$res['bot_api_id'] = $this->toSupergroup($constructor['id']);
|
||||
$res['type'] = ($constructor['megagroup'] ?? false) ? 'supergroup' : 'channel';
|
||||
$res['type'] = $constructor['megagroup'] ?? false ? 'supergroup' : 'channel';
|
||||
break;
|
||||
case 'channelForbidden':
|
||||
throw new \danog\MadelineProto\Exception('This peer is not present in the internal peer database');
|
||||
@ -696,7 +650,6 @@ trait PeerHandler
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
|
||||
/**
|
||||
* When were full info for this chat last cached.
|
||||
*
|
||||
@ -708,7 +661,6 @@ trait PeerHandler
|
||||
{
|
||||
return isset($this->full_chats[$id]['last_update']) ? $this->full_chats[$id]['last_update'] : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get full info about peer, returns an FullInfo object.
|
||||
*
|
||||
@ -720,7 +672,7 @@ trait PeerHandler
|
||||
*/
|
||||
public function getFullInfo($id): \Generator
|
||||
{
|
||||
$partial = yield $this->getInfo($id);
|
||||
$partial = (yield from $this->getInfo($id));
|
||||
if (\time() - $this->fullChatLastUpdated($partial['bot_api_id']) < (isset($this->settings['peer']['full_info_cache_time']) ? $this->settings['peer']['full_info_cache_time'] : 0)) {
|
||||
return \array_merge($partial, $this->full_chats[$partial['bot_api_id']]);
|
||||
}
|
||||
@ -737,17 +689,13 @@ trait PeerHandler
|
||||
$full = (yield $this->methodCallAsyncRead('channels.getFullChannel', ['channel' => $partial['InputChannel']], ['datacenter' => $this->datacenter->curdc]))['full_chat'];
|
||||
break;
|
||||
}
|
||||
|
||||
$res = [];
|
||||
$res['full'] = $full;
|
||||
$res['last_update'] = \time();
|
||||
$this->full_chats[$partial['bot_api_id']] = $res;
|
||||
|
||||
$partial = yield $this->getInfo($id);
|
||||
|
||||
$partial = (yield from $this->getInfo($id));
|
||||
return \array_merge($partial, $res);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get full info about peer (including full list of channel members), returns a Chat object.
|
||||
*
|
||||
@ -759,7 +707,7 @@ trait PeerHandler
|
||||
*/
|
||||
public function getPwrChat($id, $fullfetch = true, $send = true): \Generator
|
||||
{
|
||||
$full = $fullfetch ? yield $this->getFullInfo($id) : yield $this->getInfo($id);
|
||||
$full = $fullfetch ? yield from $this->getFullInfo($id) : (yield from $this->getInfo($id));
|
||||
$res = ['id' => $full['bot_api_id'], 'type' => $full['type']];
|
||||
switch ($full['type']) {
|
||||
case 'user':
|
||||
@ -828,15 +776,15 @@ trait PeerHandler
|
||||
if (isset($res['participants']) && $fullfetch) {
|
||||
foreach ($res['participants'] as $key => $participant) {
|
||||
$newres = [];
|
||||
$newres['user'] = yield $this->getPwrChat($participant['user_id'], false, true);
|
||||
$newres['user'] = (yield from $this->getPwrChat($participant['user_id'], false, true));
|
||||
if (isset($participant['inviter_id'])) {
|
||||
$newres['inviter'] = yield $this->getPwrChat($participant['inviter_id'], false, true);
|
||||
$newres['inviter'] = (yield from $this->getPwrChat($participant['inviter_id'], false, true));
|
||||
}
|
||||
if (isset($participant['promoted_by'])) {
|
||||
$newres['promoted_by'] = yield $this->getPwrChat($participant['promoted_by'], false, true);
|
||||
$newres['promoted_by'] = (yield from $this->getPwrChat($participant['promoted_by'], false, true));
|
||||
}
|
||||
if (isset($participant['kicked_by'])) {
|
||||
$newres['kicked_by'] = yield $this->getPwrChat($participant['kicked_by'], false, true);
|
||||
$newres['kicked_by'] = (yield from $this->getPwrChat($participant['kicked_by'], false, true));
|
||||
}
|
||||
if (isset($participant['date'])) {
|
||||
$newres['date'] = $participant['date'];
|
||||
@ -873,15 +821,14 @@ trait PeerHandler
|
||||
$limit = 200;
|
||||
$filters = ['channelParticipantsAdmins', 'channelParticipantsBots'];
|
||||
foreach ($filters as $filter) {
|
||||
yield $this->fetchParticipants($full['InputChannel'], $filter, '', $total_count, $res);
|
||||
yield from $this->fetchParticipants($full['InputChannel'], $filter, '', $total_count, $res);
|
||||
}
|
||||
$q = '';
|
||||
|
||||
$filters = ['channelParticipantsSearch', 'channelParticipantsKicked', 'channelParticipantsBanned'];
|
||||
foreach ($filters as $filter) {
|
||||
yield $this->recurseAlphabetSearchParticipants($full['InputChannel'], $filter, $q, $total_count, $res);
|
||||
yield from $this->recurseAlphabetSearchParticipants($full['InputChannel'], $filter, $q, $total_count, $res);
|
||||
}
|
||||
$this->logger->logger('Fetched '.\count($res['participants'])." out of $total_count");
|
||||
$this->logger->logger('Fetched ' . \count($res['participants']) . " out of {$total_count}");
|
||||
$res['participants'] = \array_values($res['participants']);
|
||||
}
|
||||
if (!$fullfetch) {
|
||||
@ -890,64 +837,55 @@ trait PeerHandler
|
||||
if ($fullfetch || $send) {
|
||||
$this->storeDb($res);
|
||||
}
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
private function recurseAlphabetSearchParticipants($channel, $filter, $q, $total_count, &$res)
|
||||
private function recurseAlphabetSearchParticipants($channel, $filter, $q, $total_count, &$res): \Generator
|
||||
{
|
||||
if (!yield $this->fetchParticipants($channel, $filter, $q, $total_count, $res)) {
|
||||
if (!(yield from $this->fetchParticipants($channel, $filter, $q, $total_count, $res))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for ($x = 'a'; $x !== 'aa' && $total_count > \count($res['participants']); $x++) {
|
||||
yield $this->recurseAlphabetSearchParticipants($channel, $filter, $q.$x, $total_count, $res);
|
||||
yield from $this->recurseAlphabetSearchParticipants($channel, $filter, $q . $x, $total_count, $res);
|
||||
}
|
||||
}
|
||||
|
||||
private function fetchParticipants($channel, $filter, $q, $total_count, &$res)
|
||||
private function fetchParticipants($channel, $filter, $q, $total_count, &$res): \Generator
|
||||
{
|
||||
$offset = 0;
|
||||
$limit = 200;
|
||||
$has_more = false;
|
||||
$cached = false;
|
||||
$last_count = -1;
|
||||
|
||||
do {
|
||||
try {
|
||||
$gres = yield $this->methodCallAsyncRead('channels.getParticipants', ['channel' => $channel, 'filter' => ['_' => $filter, 'q' => $q], 'offset' => $offset, 'limit' => $limit, 'hash' => $hash = $this->getParticipantsHash($channel, $filter, $q, $offset, $limit)], ['datacenter' => $this->datacenter->curdc, 'heavy' => true]);
|
||||
} catch (\danog\MadelineProto\RPCErrorException $e) {
|
||||
if ($e->rpc === 'CHAT_ADMIN_REQUIRED') {
|
||||
$this->logger->logger($e->rpc);
|
||||
|
||||
return $has_more;
|
||||
}
|
||||
throw $e;
|
||||
}
|
||||
|
||||
if ($cached = $gres['_'] === 'channels.channelParticipantsNotModified') {
|
||||
$gres = $this->fetchParticipantsCache($channel, $filter, $q, $offset, $limit);
|
||||
} else {
|
||||
$this->storeParticipantsCache($gres, $channel, $filter, $q, $offset, $limit);
|
||||
}
|
||||
|
||||
if ($last_count !== -1 && $last_count !== $gres['count']) {
|
||||
$has_more = true;
|
||||
} else {
|
||||
$last_count = $gres['count'];
|
||||
}
|
||||
|
||||
foreach ($gres['participants'] as $participant) {
|
||||
$newres = [];
|
||||
$newres['user'] = yield $this->getPwrChat($participant['user_id'], false, true);
|
||||
$newres['user'] = (yield from $this->getPwrChat($participant['user_id'], false, true));
|
||||
if (isset($participant['inviter_id'])) {
|
||||
$newres['inviter'] = yield $this->getPwrChat($participant['inviter_id'], false, true);
|
||||
$newres['inviter'] = (yield from $this->getPwrChat($participant['inviter_id'], false, true));
|
||||
}
|
||||
if (isset($participant['kicked_by'])) {
|
||||
$newres['kicked_by'] = yield $this->getPwrChat($participant['kicked_by'], false, true);
|
||||
$newres['kicked_by'] = (yield from $this->getPwrChat($participant['kicked_by'], false, true));
|
||||
}
|
||||
if (isset($participant['promoted_by'])) {
|
||||
$newres['promoted_by'] = yield $this->getPwrChat($participant['promoted_by'], false, true);
|
||||
$newres['promoted_by'] = (yield from $this->getPwrChat($participant['promoted_by'], false, true));
|
||||
}
|
||||
if (isset($participant['date'])) {
|
||||
$newres['date'] = $participant['date'];
|
||||
@ -980,22 +918,18 @@ trait PeerHandler
|
||||
}
|
||||
$res['participants'][$participant['user_id']] = $newres;
|
||||
}
|
||||
$this->logger->logger('Fetched '.\count($gres['participants'])." channel participants with filter $filter, query $q, offset $offset, limit $limit, hash $hash: ".($cached ? 'cached' : 'not cached').', '.($offset + \count($gres['participants'])).' participants out of '.$gres['count'].', in total fetched '.\count($res['participants']).' out of '.$total_count);
|
||||
$this->logger->logger('Fetched ' . \count($gres['participants']) . " channel participants with filter {$filter}, query {$q}, offset {$offset}, limit {$limit}, hash {$hash}: " . ($cached ? 'cached' : 'not cached') . ', ' . ($offset + \count($gres['participants'])) . ' participants out of ' . $gres['count'] . ', in total fetched ' . \count($res['participants']) . ' out of ' . $total_count);
|
||||
$offset += \count($gres['participants']);
|
||||
} while (\count($gres['participants']));
|
||||
|
||||
if ($offset === $limit) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $has_more;
|
||||
}
|
||||
|
||||
private function fetchParticipantsCache($channel, $filter, $q, $offset, $limit)
|
||||
{
|
||||
return $this->channel_participants[$channel['channel_id']][$filter][$q][$offset][$limit];
|
||||
}
|
||||
|
||||
private function storeParticipantsCache($gres, $channel, $filter, $q, $offset, $limit)
|
||||
{
|
||||
//return;
|
||||
@ -1008,13 +942,11 @@ trait PeerHandler
|
||||
$gres['hash'] = \danog\MadelineProto\Tools::genVectorHash($ids);
|
||||
$this->channel_participants[$channel['channel_id']][$filter][$q][$offset][$limit] = $gres;
|
||||
}
|
||||
|
||||
private function getParticipantsHash($channel, $filter, $q, $offset, $limit)
|
||||
{
|
||||
return isset($this->channel_participants[$channel['channel_id']][$filter][$q][$offset][$limit]) ? $this->channel_participants[$channel['channel_id']][$filter][$q][$offset][$limit]['hash'] : 0;
|
||||
}
|
||||
|
||||
private function storeDb($res, $force = false)
|
||||
private function storeDb($res, $force = false): \Generator
|
||||
{
|
||||
$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']) {
|
||||
@ -1033,29 +965,22 @@ trait PeerHandler
|
||||
if (empty($this->qres)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
$payload = \json_encode($this->qres);
|
||||
//$path = '/tmp/ids'.hash('sha256', $payload);
|
||||
//file_put_contents($path, $payload);
|
||||
$id = isset($this->authorization['user']['username']) ? $this->authorization['user']['username'] : $this->authorization['user']['id'];
|
||||
|
||||
$request = new Request('https://id.pwrtelegram.xyz/db' . $this->settings['pwr']['db_token'] . '/addnewmadeline?d=pls&from=' . $id, 'POST');
|
||||
$request->setHeader('content-type', 'application/json');
|
||||
$request->setBody($payload);
|
||||
|
||||
$result = yield (
|
||||
yield $this->datacenter->getHTTPClient()->request($request)
|
||||
)->getBody()->buffer();
|
||||
|
||||
$this->logger->logger("============ $result =============", \danog\MadelineProto\Logger::VERBOSE);
|
||||
$result = yield (yield $this->datacenter->getHTTPClient()->request($request))->getBody()->buffer();
|
||||
$this->logger->logger("============ {$result} =============", \danog\MadelineProto\Logger::VERBOSE);
|
||||
$this->qres = [];
|
||||
$this->last_stored = \time() + 10;
|
||||
} catch (\danog\MadelineProto\Exception $e) {
|
||||
$this->logger->logger('======= COULD NOT STORE IN DB DUE TO ' . $e->getMessage() . ' =============', \danog\MadelineProto\Logger::VERBOSE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve username (use getInfo instead).
|
||||
*
|
||||
@ -1073,7 +998,6 @@ trait PeerHandler
|
||||
if (\strpos($e->rpc, 'FLOOD_WAIT_') === 0 || $e->rpc === 'AUTH_KEY_UNREGISTERED' || $e->rpc === 'USERNAME_INVALID') {
|
||||
throw $e;
|
||||
}
|
||||
|
||||
return false;
|
||||
} finally {
|
||||
if (isset($this->caching_simple_username[$username])) {
|
||||
@ -1083,7 +1007,6 @@ trait PeerHandler
|
||||
if ($res['_'] === 'contacts.resolvedPeer') {
|
||||
return $res;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -38,7 +38,6 @@ class ReferenceDatabase implements TLCallback
|
||||
const PHOTO_LOCATION_LOCATION = 2;
|
||||
// DEPRECATED: Reference from a location (can only be document location)
|
||||
const DOCUMENT_LOCATION_LOCATION = 0;
|
||||
|
||||
// Peer + photo ID
|
||||
const USER_PHOTO_ORIGIN = 0;
|
||||
// Peer (default photo ID)
|
||||
@ -57,7 +56,6 @@ class ReferenceDatabase implements TLCallback
|
||||
const STICKER_SET_EMOTICON_ORIGIN = 8;
|
||||
//
|
||||
const WALLPAPER_ORIGIN = 9;
|
||||
|
||||
const LOCATION_CONTEXT = [
|
||||
//'inputFileLocation' => self::PHOTO_LOCATION_LOCATION, // DEPRECATED
|
||||
'inputDocumentFileLocation' => self::DOCUMENT_LOCATION,
|
||||
@ -65,34 +63,8 @@ class ReferenceDatabase implements TLCallback
|
||||
'inputPhoto' => self::PHOTO_LOCATION,
|
||||
'inputDocument' => self::DOCUMENT_LOCATION,
|
||||
];
|
||||
const METHOD_CONTEXT = [
|
||||
'photos.updateProfilePhoto' => self::USER_PHOTO_ORIGIN,
|
||||
'photos.getUserPhotos' => self::USER_PHOTO_ORIGIN,
|
||||
'photos.uploadProfilePhoto' => self::USER_PHOTO_ORIGIN,
|
||||
'messages.getStickers' => self::STICKER_SET_EMOTICON_ORIGIN,
|
||||
];
|
||||
const CONSTRUCTOR_CONTEXT = [
|
||||
'message' => self::MESSAGE_ORIGIN,
|
||||
'messageService' => self::MESSAGE_ORIGIN,
|
||||
|
||||
'chatFull' => self::PEER_PHOTO_ORIGIN,
|
||||
'channelFull' => self::PEER_PHOTO_ORIGIN,
|
||||
'chat' => self::PEER_PHOTO_ORIGIN,
|
||||
'channel' => self::PEER_PHOTO_ORIGIN,
|
||||
|
||||
'updateUserPhoto' => self::USER_PHOTO_ORIGIN,
|
||||
'user' => self::USER_PHOTO_ORIGIN,
|
||||
'userFull' => self::USER_PHOTO_ORIGIN,
|
||||
|
||||
'wallPaper' => self::WALLPAPER_ORIGIN,
|
||||
|
||||
'messages.savedGifs' => self::SAVED_GIFS_ORIGIN,
|
||||
|
||||
'messages.recentStickers' => self::STICKER_SET_RECENT_ORIGIN,
|
||||
'messages.favedStickers' => self::STICKER_SET_FAVED_ORIGIN,
|
||||
'messages.stickerSet' => self::STICKER_SET_ID_ORIGIN,
|
||||
'document' => self::STICKER_SET_ID_ORIGIN,
|
||||
];
|
||||
const METHOD_CONTEXT = ['photos.updateProfilePhoto' => self::USER_PHOTO_ORIGIN, 'photos.getUserPhotos' => self::USER_PHOTO_ORIGIN, 'photos.uploadProfilePhoto' => self::USER_PHOTO_ORIGIN, 'messages.getStickers' => self::STICKER_SET_EMOTICON_ORIGIN];
|
||||
const CONSTRUCTOR_CONTEXT = ['message' => self::MESSAGE_ORIGIN, 'messageService' => self::MESSAGE_ORIGIN, 'chatFull' => self::PEER_PHOTO_ORIGIN, 'channelFull' => self::PEER_PHOTO_ORIGIN, 'chat' => self::PEER_PHOTO_ORIGIN, 'channel' => self::PEER_PHOTO_ORIGIN, 'updateUserPhoto' => self::USER_PHOTO_ORIGIN, 'user' => self::USER_PHOTO_ORIGIN, 'userFull' => self::USER_PHOTO_ORIGIN, 'wallPaper' => self::WALLPAPER_ORIGIN, 'messages.savedGifs' => self::SAVED_GIFS_ORIGIN, 'messages.recentStickers' => self::STICKER_SET_RECENT_ORIGIN, 'messages.favedStickers' => self::STICKER_SET_FAVED_ORIGIN, 'messages.stickerSet' => self::STICKER_SET_ID_ORIGIN, 'document' => self::STICKER_SET_ID_ORIGIN];
|
||||
/**
|
||||
* References indexed by location.
|
||||
*
|
||||
@ -105,66 +77,52 @@ class ReferenceDatabase implements TLCallback
|
||||
private $API;
|
||||
private $refresh = false;
|
||||
private $refreshCount = 0;
|
||||
|
||||
public function __construct(MTProto $API)
|
||||
{
|
||||
$this->API = $API;
|
||||
$this->init();
|
||||
}
|
||||
|
||||
public function __wakeup()
|
||||
{
|
||||
$this->init();
|
||||
}
|
||||
|
||||
public function __sleep()
|
||||
{
|
||||
return ['db', 'API'];
|
||||
}
|
||||
|
||||
public function init()
|
||||
{
|
||||
foreach ($this->db as $key => $value) {
|
||||
if ($key[0] === "0") { // Unsetting deprecated DOCUMENT_LOCATION_LOCATION
|
||||
if ($key[0] === "0") {
|
||||
// Unsetting deprecated DOCUMENT_LOCATION_LOCATION
|
||||
unset($this->db[$key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getMethodCallbacks(): array
|
||||
{
|
||||
return \array_fill_keys(\array_keys(self::METHOD_CONTEXT), [[$this, 'addOriginMethod']]);
|
||||
}
|
||||
|
||||
public function getMethodBeforeCallbacks(): array
|
||||
{
|
||||
return \array_fill_keys(\array_keys(self::METHOD_CONTEXT), [[$this, 'addOriginMethodContext']]);
|
||||
}
|
||||
|
||||
public function getConstructorCallbacks(): array
|
||||
{
|
||||
return \array_merge(
|
||||
\array_fill_keys(['document', 'photo', 'fileLocation'], [[$this, 'addReference']]),
|
||||
\array_fill_keys(\array_keys(self::CONSTRUCTOR_CONTEXT), [[$this, 'addOrigin']]),
|
||||
['document' => [[$this, 'addReference'], [$this, 'addOrigin']]]
|
||||
);
|
||||
return \array_merge(\array_fill_keys(['document', 'photo', 'fileLocation'], [[$this, 'addReference']]), \array_fill_keys(\array_keys(self::CONSTRUCTOR_CONTEXT), [[$this, 'addOrigin']]), ['document' => [[$this, 'addReference'], [$this, 'addOrigin']]]);
|
||||
}
|
||||
|
||||
public function getConstructorBeforeCallbacks(): array
|
||||
{
|
||||
return \array_fill_keys(\array_keys(self::CONSTRUCTOR_CONTEXT), [[$this, 'addOriginContext']]);
|
||||
}
|
||||
|
||||
public function getConstructorSerializeCallbacks(): array
|
||||
{
|
||||
return \array_fill_keys(\array_keys(self::LOCATION_CONTEXT), [$this, 'populateReference']);
|
||||
}
|
||||
|
||||
public function getTypeMismatchCallbacks(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function reset()
|
||||
{
|
||||
if ($this->cacheContexts) {
|
||||
@ -176,7 +134,6 @@ class ReferenceDatabase implements TLCallback
|
||||
$this->cache = [];
|
||||
}
|
||||
}
|
||||
|
||||
public function addReference(array $location)
|
||||
{
|
||||
if (!$this->cacheContexts) {
|
||||
@ -189,18 +146,15 @@ class ReferenceDatabase implements TLCallback
|
||||
if ($frame['args'][1]['subtype'] === $previous) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$frames[] = $frame['args'][1]['subtype'];
|
||||
$previous = $frame['args'][1]['subtype'];
|
||||
} elseif (isset($frame['args'][1]['type'])) {
|
||||
if ($frame['args'][1]['type'] === '') {
|
||||
break;
|
||||
}
|
||||
|
||||
if ($frame['args'][1]['type'] === $previous) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$frames[] = $frame['args'][1]['type'];
|
||||
$previous = $frame['args'][1]['type'];
|
||||
}
|
||||
@ -212,12 +166,10 @@ class ReferenceDatabase implements TLCallback
|
||||
$tl_trace .= "['" . $frame . "']";
|
||||
}
|
||||
$this->API->logger->logger($tl_trace, \danog\MadelineProto\Logger::ERROR);
|
||||
|
||||
return false;
|
||||
}
|
||||
if (!isset($location['file_reference'])) {
|
||||
$this->API->logger->logger("Object {$location['_']} does not have reference", \danog\MadelineProto\Logger::ERROR);
|
||||
|
||||
return false;
|
||||
}
|
||||
$key = \count($this->cacheContexts) - 1;
|
||||
@ -234,25 +186,22 @@ class ReferenceDatabase implements TLCallback
|
||||
default:
|
||||
throw new Exception('Unknown location type provided: ' . $location['_']);
|
||||
}
|
||||
$this->API->logger->logger("Caching reference from location of type $locationType from {$location['_']}", \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
$this->API->logger->logger("Caching reference from location of type {$locationType} from {$location['_']}", \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
if (!isset($this->cache[$key])) {
|
||||
$this->cache[$key] = [];
|
||||
}
|
||||
$this->cache[$key][$this->serializeLocation($locationType, $location)] = (string) $location['file_reference'];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function addOriginContext(string $type)
|
||||
{
|
||||
if (!isset(self::CONSTRUCTOR_CONTEXT[$type])) {
|
||||
throw new \danog\MadelineProto\Exception("Unknown origin type provided: $type");
|
||||
throw new \danog\MadelineProto\Exception("Unknown origin type provided: {$type}");
|
||||
}
|
||||
$originContext = self::CONSTRUCTOR_CONTEXT[$type];
|
||||
$this->API->logger->logger("Adding origin context $originContext for {$type}!", \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
$this->API->logger->logger("Adding origin context {$originContext} for {$type}!", \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
$this->cacheContexts[] = $originContext;
|
||||
}
|
||||
|
||||
public function addOrigin(array $data = [])
|
||||
{
|
||||
$key = \count($this->cacheContexts) - 1;
|
||||
@ -261,8 +210,7 @@ class ReferenceDatabase implements TLCallback
|
||||
}
|
||||
$originType = \array_pop($this->cacheContexts);
|
||||
if (!isset($this->cache[$key])) {
|
||||
$this->API->logger->logger("Removing origin context $originType for {$data['_']}, nothing in the reference cache!", \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
|
||||
$this->API->logger->logger("Removing origin context {$originType} for {$data['_']}, nothing in the reference cache!", \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
return;
|
||||
}
|
||||
$cache = $this->cache[$key];
|
||||
@ -316,12 +264,10 @@ class ReferenceDatabase implements TLCallback
|
||||
if (!isset($this->cache[$key])) {
|
||||
$this->cache[$key] = [];
|
||||
}
|
||||
|
||||
foreach ($cache as $location => $reference) {
|
||||
$this->cache[$key][$location] = $reference;
|
||||
}
|
||||
$this->API->logger->logger("Skipped origin $originType ({$data['_']}) for ".\count($cache).' references', \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
|
||||
$this->API->logger->logger("Skipped origin {$originType} ({$data['_']}) for " . \count($cache) . ' references', \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
@ -334,19 +280,17 @@ class ReferenceDatabase implements TLCallback
|
||||
foreach ($cache as $location => $reference) {
|
||||
$this->storeReference($location, $reference, $originType, $origin);
|
||||
}
|
||||
$this->API->logger->logger("Added origin $originType ({$data['_']}) to ".\count($cache).' references', \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
$this->API->logger->logger("Added origin {$originType} ({$data['_']}) to " . \count($cache) . ' references', \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
}
|
||||
|
||||
public function addOriginMethodContext(string $type)
|
||||
{
|
||||
if (!isset(self::METHOD_CONTEXT[$type])) {
|
||||
throw new \danog\MadelineProto\Exception("Unknown origin type provided: {$type}");
|
||||
}
|
||||
$originContext = self::METHOD_CONTEXT[$type];
|
||||
$this->API->logger->logger("Adding origin context $originContext for {$type}!", \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
$this->API->logger->logger("Adding origin context {$originContext} for {$type}!", \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
$this->cacheContexts[] = $originContext;
|
||||
}
|
||||
|
||||
public function addOriginMethod(array $data, array $res)
|
||||
{
|
||||
$key = \count($this->cacheContexts) - 1;
|
||||
@ -355,8 +299,7 @@ class ReferenceDatabase implements TLCallback
|
||||
}
|
||||
$originType = \array_pop($this->cacheContexts);
|
||||
if (!isset($this->cache[$key])) {
|
||||
$this->API->logger->logger("Removing origin context $originType for {$data['_']}, nothing in the reference cache!", \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
|
||||
$this->API->logger->logger("Removing origin context {$originType} for {$data['_']}, nothing in the reference cache!", \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
return;
|
||||
}
|
||||
$cache = $this->cache[$key];
|
||||
@ -384,16 +327,13 @@ class ReferenceDatabase implements TLCallback
|
||||
foreach ($res['photos'] as $photo) {
|
||||
$origin['max_id'] = $photo['id'];
|
||||
$dc_id = $photo['dc_id'];
|
||||
|
||||
$location = $this->serializeLocation(self::PHOTO_LOCATION, $photo);
|
||||
if (isset($cache[$location])) {
|
||||
$reference = $cache[$location];
|
||||
unset($cache[$location]);
|
||||
|
||||
$this->storeReference($location, $reference, $originType, $origin);
|
||||
$count++;
|
||||
}
|
||||
|
||||
if (isset($photo['sizes'])) {
|
||||
foreach ($photo['sizes'] as $size) {
|
||||
if (isset($size['location'])) {
|
||||
@ -409,8 +349,7 @@ class ReferenceDatabase implements TLCallback
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->API->logger->logger("Added origin $originType ({$data['_']}) to $count references", \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
|
||||
$this->API->logger->logger("Added origin {$originType} ({$data['_']}) to {$count} references", \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
return;
|
||||
case 'messages.getStickers':
|
||||
$origin['emoticon'] = $body['emoticon'];
|
||||
@ -421,9 +360,8 @@ class ReferenceDatabase implements TLCallback
|
||||
foreach ($cache as $location => $reference) {
|
||||
$this->storeReference($location, $reference, $originType, $origin);
|
||||
}
|
||||
$this->API->logger->logger("Added origin $originType ({$data['_']}) to ".\count($cache).' references', \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
$this->API->logger->logger("Added origin {$originType} ({$data['_']}) to " . \count($cache) . ' references', \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
}
|
||||
|
||||
public function storeReference(string $location, string $reference, int $originType, array $origin)
|
||||
{
|
||||
if (!isset($this->db[$location])) {
|
||||
@ -431,17 +369,14 @@ class ReferenceDatabase implements TLCallback
|
||||
}
|
||||
$this->db[$location]['reference'] = $reference;
|
||||
$this->db[$location]['origins'][$originType] = $origin;
|
||||
|
||||
if ($this->refresh) {
|
||||
$this->refreshed[$location] = true;
|
||||
}
|
||||
|
||||
$key = \count($this->cacheContexts) - 1;
|
||||
if ($key >= 0) {
|
||||
$this->cache[$key][$location] = $reference;
|
||||
}
|
||||
}
|
||||
|
||||
public function refreshNext($refresh = false)
|
||||
{
|
||||
if ($this->refreshCount === 1 && !$refresh) {
|
||||
@ -459,23 +394,18 @@ class ReferenceDatabase implements TLCallback
|
||||
$this->refreshCount--;
|
||||
}
|
||||
}
|
||||
|
||||
public function refreshReference(int $locationType, array $location)
|
||||
{
|
||||
return $this->refreshReferenceInternal($this->serializeLocation($locationType, $location));
|
||||
}
|
||||
|
||||
public function refreshReferenceInternal(string $location)
|
||||
public function refreshReferenceInternal(string $location): \Generator
|
||||
{
|
||||
if (isset($this->refreshed[$location])) {
|
||||
$this->API->logger->logger('Reference already refreshed!', \danog\MadelineProto\Logger::VERBOSE);
|
||||
|
||||
return $this->db[$location]['reference'];
|
||||
}
|
||||
|
||||
\ksort($this->db[$location]['origins']);
|
||||
$count = 0;
|
||||
|
||||
foreach ($this->db[$location]['origins'] as $originType => &$origin) {
|
||||
$count++;
|
||||
$this->API->logger->logger("Try {$count} refreshing file reference with origin type {$originType}", \danog\MadelineProto\Logger::VERBOSE);
|
||||
@ -521,50 +451,39 @@ class ReferenceDatabase implements TLCallback
|
||||
yield $this->API->methodCallAsyncRead('account.getWallPapers', $origin, ['datacenter' => $this->API->settings['connection_settings']['default_dc']]);
|
||||
break;
|
||||
default:
|
||||
throw new \danog\MadelineProto\Exception("Unknown origin type $originType");
|
||||
throw new \danog\MadelineProto\Exception("Unknown origin type {$originType}");
|
||||
}
|
||||
if (isset($this->refreshed[$location])) {
|
||||
return $this->db[$location]['reference'];
|
||||
}
|
||||
}
|
||||
|
||||
throw new Exception('Did not refresh reference');
|
||||
}
|
||||
|
||||
public function populateReference(array $object)
|
||||
public function populateReference(array $object): \Generator
|
||||
{
|
||||
$object['file_reference'] = yield $this->getReference(self::LOCATION_CONTEXT[$object['_']], $object);
|
||||
|
||||
return $object;
|
||||
}
|
||||
|
||||
public function getReference(int $locationType, array $location)
|
||||
{
|
||||
$locationString = $this->serializeLocation($locationType, $location);
|
||||
if (!isset($this->db[$locationString]['reference'])) {
|
||||
if (isset($location['file_reference'])) {
|
||||
$this->API->logger->logger("Using outdated file reference for location of type $locationType object {$location['_']}", \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
|
||||
$this->API->logger->logger("Using outdated file reference for location of type {$locationType} object {$location['_']}", \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
return $location['file_reference'];
|
||||
}
|
||||
|
||||
if (!$this->refresh) {
|
||||
$this->API->logger->logger("Using null file reference for location of type $locationType object {$location['_']}", \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
|
||||
$this->API->logger->logger("Using null file reference for location of type {$locationType} object {$location['_']}", \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
return '';
|
||||
}
|
||||
|
||||
throw new \danog\MadelineProto\Exception("Could not find file reference for location of type $locationType object {$location['_']}");
|
||||
throw new \danog\MadelineProto\Exception("Could not find file reference for location of type {$locationType} object {$location['_']}");
|
||||
}
|
||||
$this->API->logger->logger("Getting file reference for location of type $locationType object {$location['_']}", \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
|
||||
$this->API->logger->logger("Getting file reference for location of type {$locationType} object {$location['_']}", \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
if ($this->refresh) {
|
||||
return $this->refreshReferenceInternal($locationString);
|
||||
}
|
||||
|
||||
return $this->db[$locationString]['reference'];
|
||||
}
|
||||
|
||||
private function serializeLocation(int $locationType, array $location)
|
||||
{
|
||||
switch ($locationType) {
|
||||
@ -575,11 +494,9 @@ class ReferenceDatabase implements TLCallback
|
||||
$dc_id = \danog\MadelineProto\Tools::packSignedInt($location['dc_id']);
|
||||
$volume_id = \is_int($location['volume_id']) ? \danog\MadelineProto\Tools::packSignedLong($location['volume_id']) : $location['volume_id'];
|
||||
$local_id = \danog\MadelineProto\Tools::packSignedInt($location['local_id']);
|
||||
|
||||
return $locationType . $dc_id . $volume_id . $local_id;
|
||||
}
|
||||
}
|
||||
|
||||
public function __debugInfo()
|
||||
{
|
||||
return ['ReferenceDatabase instance ' . \spl_object_hash($this)];
|
||||
|
@ -34,7 +34,6 @@ trait UpdateHandler
|
||||
private $channels_state;
|
||||
public $updates = [];
|
||||
public $updates_key = 0;
|
||||
|
||||
/**
|
||||
* PWR update handler.
|
||||
*
|
||||
@ -56,7 +55,6 @@ trait UpdateHandler
|
||||
\in_array($this->settings['pwr']['updateHandler'], [['danog\\MadelineProto\\API', 'getUpdatesUpdateHandler'], 'getUpdatesUpdateHandler']) ? $this->getUpdatesUpdateHandler($update) : $this->settings['pwr']['updateHandler']($update);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Getupdates update handler.
|
||||
*
|
||||
@ -73,7 +71,6 @@ trait UpdateHandler
|
||||
}
|
||||
$this->updates[$this->updates_key++] = $update;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get updates.
|
||||
*
|
||||
@ -92,9 +89,7 @@ trait UpdateHandler
|
||||
if (!$this->settings['updates']['run_callback']) {
|
||||
$this->settings['updates']['run_callback'] = true;
|
||||
}
|
||||
|
||||
$params = \array_merge(self::DEFAULT_GETUPDATES_PARAMS, $params);
|
||||
|
||||
if (empty($this->updates)) {
|
||||
$this->update_deferred = new Deferred();
|
||||
if (!$params['timeout']) {
|
||||
@ -102,11 +97,9 @@ trait UpdateHandler
|
||||
}
|
||||
yield \danog\MadelineProto\Tools::first([$this->waitUpdate(), \danog\MadelineProto\Tools::sleep($params['timeout'])]);
|
||||
}
|
||||
|
||||
if (empty($this->updates)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if ($params['offset'] < 0) {
|
||||
$params['offset'] = \array_reverse(\array_keys((array) $this->updates))[\abs($params['offset']) - 1];
|
||||
}
|
||||
@ -118,13 +111,10 @@ trait UpdateHandler
|
||||
$updates[] = ['update_id' => $key, 'update' => $value];
|
||||
}
|
||||
}
|
||||
|
||||
return $updates;
|
||||
}
|
||||
|
||||
public $update_resolved = false;
|
||||
public $update_deferred;
|
||||
|
||||
/**
|
||||
* Wait for update.
|
||||
*
|
||||
@ -141,7 +131,6 @@ trait UpdateHandler
|
||||
$this->update_resolved = false;
|
||||
$this->update_deferred = new Deferred();
|
||||
}
|
||||
|
||||
/**
|
||||
* Signal update.
|
||||
*
|
||||
@ -161,8 +150,6 @@ trait UpdateHandler
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check message ID.
|
||||
*
|
||||
@ -177,7 +164,6 @@ trait UpdateHandler
|
||||
if (!isset($message['to_id'])) {
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
$peer_id = $this->getId($message['to_id']);
|
||||
} catch (\danog\MadelineProto\Exception $e) {
|
||||
@ -188,13 +174,10 @@ trait UpdateHandler
|
||||
$message_id = $message['id'];
|
||||
if (!isset($this->msg_ids[$peer_id]) || $message_id > $this->msg_ids[$peer_id]) {
|
||||
$this->msg_ids[$peer_id] = $message_id;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get channel state.
|
||||
*
|
||||
@ -202,16 +185,14 @@ trait UpdateHandler
|
||||
*
|
||||
* @return UpdatesState|UpdatesState[]
|
||||
*/
|
||||
public function loadUpdateState()
|
||||
public function loadUpdateState(): \Generator
|
||||
{
|
||||
if (!$this->got_state) {
|
||||
$this->got_state = true;
|
||||
$this->channels_state->get(false, yield $this->getUpdatesState());
|
||||
$this->channels_state->get(false, yield from $this->getUpdatesState());
|
||||
}
|
||||
|
||||
return $this->channels_state->get(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load channel state.
|
||||
*
|
||||
@ -226,7 +207,6 @@ trait UpdateHandler
|
||||
{
|
||||
return $this->channels_state->get($channelId, $init);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get channel states.
|
||||
*
|
||||
@ -238,7 +218,6 @@ trait UpdateHandler
|
||||
{
|
||||
return $this->channels_state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get update state.
|
||||
*
|
||||
@ -250,11 +229,8 @@ trait UpdateHandler
|
||||
{
|
||||
$data = yield $this->methodCallAsyncRead('updates.getState', [], ['datacenter' => $this->settings['connection_settings']['default_dc']]);
|
||||
yield $this->getCdnConfig($this->settings['connection_settings']['default_dc']);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Undocumented function.
|
||||
*
|
||||
@ -279,10 +255,7 @@ trait UpdateHandler
|
||||
case 'updatesCombined':
|
||||
$result = [];
|
||||
foreach ($updates['updates'] as $key => $update) {
|
||||
if ($update['_'] === 'updateNewMessage' || $update['_'] === 'updateReadMessagesContents' ||
|
||||
$update['_'] === 'updateEditMessage' || $update['_'] === 'updateDeleteMessages' ||
|
||||
$update['_'] === 'updateReadHistoryInbox' || $update['_'] === 'updateReadHistoryOutbox' ||
|
||||
$update['_'] === 'updateWebPage' || $update['_'] === 'updateMessageID') {
|
||||
if ($update['_'] === 'updateNewMessage' || $update['_'] === 'updateReadMessagesContents' || $update['_'] === 'updateEditMessage' || $update['_'] === 'updateDeleteMessages' || $update['_'] === 'updateReadHistoryInbox' || $update['_'] === 'updateReadHistoryOutbox' || $update['_'] === 'updateWebPage' || $update['_'] === 'updateMessageID') {
|
||||
$result[yield $this->feeders[false]->feedSingle($update)] = true;
|
||||
unset($updates['updates'][$key]);
|
||||
}
|
||||
@ -313,15 +286,13 @@ trait UpdateHandler
|
||||
case 'updateShortChatMessage':
|
||||
$from_id = isset($updates['from_id']) ? $updates['from_id'] : ($updates['out'] ? $this->authorization['user']['id'] : $updates['user_id']);
|
||||
$to_id = isset($updates['chat_id']) ? -$updates['chat_id'] : ($updates['out'] ? $updates['user_id'] : $this->authorization['user']['id']);
|
||||
if (!yield $this->peerIsset($from_id) || !yield $this->peerIsset($to_id) || isset($updates['via_bot_id']) && !yield $this->peerIsset($updates['via_bot_id']) || isset($updates['entities']) && !yield $this->entitiesPeerIsset($updates['entities']) || isset($updates['fwd_from']) && !yield $this->fwdPeerIsset($updates['fwd_from'])) {
|
||||
if (!(yield from $this->peerIsset($from_id) || !(yield from $this->peerIsset($to_id) || isset($updates['via_bot_id']) && !(yield from $this->peerIsset($updates['via_bot_id']) || isset($updates['entities']) && !(yield from $this->entitiesPeerIsset($updates['entities']) || isset($updates['fwd_from']) && !yield $this->fwdPeerIsset($updates['fwd_from'])))))) {
|
||||
yield $this->updaters[false]->resume();
|
||||
|
||||
return;
|
||||
}
|
||||
$message = $updates;
|
||||
$message['_'] = 'message';
|
||||
$message['from_id'] = $from_id;
|
||||
|
||||
try {
|
||||
$message['to_id'] = (yield $this->getInfo($to_id))['Peer'];
|
||||
} catch (\danog\MadelineProto\Exception $e) {
|
||||
@ -368,13 +339,11 @@ trait UpdateHandler
|
||||
$this->logger->logger('Got new dc options', \danog\MadelineProto\Logger::VERBOSE);
|
||||
$this->config['dc_options'] = $update['dc_options'];
|
||||
yield $this->parseConfig();
|
||||
|
||||
return;
|
||||
}
|
||||
if ($update['_'] === 'updatePhoneCall') {
|
||||
if (!\class_exists('\\danog\\MadelineProto\\VoIP')) {
|
||||
$this->logger->logger('The php-libtgvoip extension is required to accept and manage calls. See daniil.it/MadelineProto for more info.', \danog\MadelineProto\Logger::WARNING);
|
||||
|
||||
return;
|
||||
}
|
||||
switch ($update['phone_call']['_']) {
|
||||
@ -403,32 +372,28 @@ trait UpdateHandler
|
||||
if (!isset($this->calls[$update['phone_call']['id']])) {
|
||||
return;
|
||||
}
|
||||
|
||||
return $this->calls[$update['phone_call']['id']]->discard($update['phone_call']['reason'], [], $update['phone_call']['need_debug']);
|
||||
}
|
||||
}
|
||||
if ($update['_'] === 'updateNewEncryptedMessage' && !isset($update['message']['decrypted_message'])) {
|
||||
if (isset($update['qts'])) {
|
||||
$cur_state = yield $this->loadUpdateState();
|
||||
$cur_state = (yield from $this->loadUpdateState());
|
||||
if ($cur_state->qts() === -1) {
|
||||
$cur_state->qts($update['qts']);
|
||||
}
|
||||
if ($update['qts'] < $cur_state->qts()) {
|
||||
$this->logger->logger('Duplicate update. update qts: ' . $update['qts'] . ' <= current qts ' . $cur_state->qts() . ', chat id: ' . $update['message']['chat_id'], \danog\MadelineProto\Logger::ERROR);
|
||||
|
||||
return false;
|
||||
}
|
||||
if ($update['qts'] > $cur_state->qts() + 1) {
|
||||
$this->logger->logger('Qts hole. Fetching updates manually: update qts: ' . $update['qts'] . ' > current qts ' . $cur_state->qts() . '+1, chat id: ' . $update['message']['chat_id'], \danog\MadelineProto\Logger::ERROR);
|
||||
$this->updaters[false]->resumeDefer();
|
||||
|
||||
return false;
|
||||
}
|
||||
$this->logger->logger('Applying qts: ' . $update['qts'] . ' over current qts ' . $cur_state->qts() . ', chat id: ' . $update['message']['chat_id'], \danog\MadelineProto\Logger::VERBOSE);
|
||||
yield $this->methodCallAsyncRead('messages.receivedQueue', ['max_qts' => $cur_state->qts($update['qts'])], ['datacenter' => $this->settings['connection_settings']['default_dc']]);
|
||||
}
|
||||
yield $this->handleEncryptedUpdate($update);
|
||||
|
||||
return;
|
||||
}
|
||||
/*
|
||||
@ -446,7 +411,7 @@ trait UpdateHandler
|
||||
try {
|
||||
yield $this->acceptSecretChat($update['chat']);
|
||||
} catch (RPCErrorException $e) {
|
||||
$this->logger->logger("Error while accepting secret chat: $e", Logger::FATAL_ERROR);
|
||||
$this->logger->logger("Error while accepting secret chat: {$e}", Logger::FATAL_ERROR);
|
||||
}
|
||||
break;
|
||||
case 'encryptedChatDiscarded':
|
||||
@ -460,7 +425,6 @@ trait UpdateHandler
|
||||
if (isset($this->temp_rekeyed_secret_chats[$update['chat']['id']])) {
|
||||
unset($this->temp_rekeyed_secret_chats[$update['chat']['id']]);
|
||||
}
|
||||
|
||||
break;
|
||||
case 'encryptedChat':
|
||||
$this->logger->logger('Completing creation of secret chat ' . $update['chat']['id'], \danog\MadelineProto\Logger::NOTICE);
|
||||
@ -470,7 +434,6 @@ trait UpdateHandler
|
||||
//$this->logger->logger($update, \danog\MadelineProto\Logger::NOTICE);
|
||||
}
|
||||
//if ($update['_'] === 'updateServiceNotification' && strpos($update['type'], 'AUTH_KEY_DROP_') === 0) {
|
||||
|
||||
//}
|
||||
if (!$this->settings['updates']['handle_updates']) {
|
||||
return;
|
||||
@ -488,7 +451,6 @@ trait UpdateHandler
|
||||
$this->getUpdatesUpdateHandler($update);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send update to webhook.
|
||||
*
|
||||
@ -502,23 +464,20 @@ trait UpdateHandler
|
||||
//$this->logger->logger($update, $payload, json_last_error());
|
||||
if ($payload === '') {
|
||||
$this->logger->logger('EMPTY UPDATE');
|
||||
|
||||
return;
|
||||
}
|
||||
\danog\MadelineProto\Tools::callFork((function () use ($payload) {
|
||||
\danog\MadelineProto\Tools::callFork((function () use ($payload): \Generator {
|
||||
$request = new Request($this->hook_url, 'POST');
|
||||
$request->setHeader('content-type', 'application/json');
|
||||
$request->setBody($payload);
|
||||
|
||||
$result = yield (yield $this->datacenter->getHTTPClient()->request($request))->getBody()->buffer();
|
||||
|
||||
$this->logger->logger('Result of webhook query is ' . $result, \danog\MadelineProto\Logger::NOTICE);
|
||||
$result = \json_decode($result, true);
|
||||
if (\is_array($result) && isset($result['method']) && $result['method'] != '' && \is_string($result['method'])) {
|
||||
try {
|
||||
$this->logger->logger('Reverse webhook command returned', yield $this->methodCallAsyncRead($result['method'], $result, ['datacenter' => $this->datacenter->curdc]));
|
||||
} catch (\Throwable $e) {
|
||||
$this->logger->logger("Reverse webhook command returned: $e");
|
||||
$this->logger->logger("Reverse webhook command returned: {$e}");
|
||||
}
|
||||
}
|
||||
})());
|
||||
|
@ -48,21 +48,18 @@ class UpdatesState
|
||||
* @var int
|
||||
*/
|
||||
private $date = 1;
|
||||
|
||||
/**
|
||||
* Channel ID.
|
||||
*
|
||||
* @var int|bool
|
||||
*/
|
||||
private $channelId;
|
||||
|
||||
/**
|
||||
* Is busy?
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private $syncLoading = false;
|
||||
|
||||
/**
|
||||
* Init function.
|
||||
*
|
||||
@ -74,7 +71,6 @@ class UpdatesState
|
||||
$this->channelId = $channelId;
|
||||
$this->update($init);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sleep function.
|
||||
*
|
||||
@ -84,7 +80,6 @@ class UpdatesState
|
||||
{
|
||||
return $this->channelId ? ['pts', 'channelId'] : ['pts', 'qts', 'seq', 'date', 'channelId'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this state relative to a channel?
|
||||
*
|
||||
@ -94,7 +89,6 @@ class UpdatesState
|
||||
{
|
||||
return (bool) $this->channelId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the channel ID.
|
||||
*
|
||||
@ -104,7 +98,6 @@ class UpdatesState
|
||||
{
|
||||
return $this->channelId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Are we currently busy?
|
||||
*
|
||||
@ -117,10 +110,8 @@ class UpdatesState
|
||||
if ($set !== null) {
|
||||
$this->syncLoading = $set;
|
||||
}
|
||||
|
||||
return $this->syncLoading;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update multiple parameters.
|
||||
*
|
||||
@ -135,10 +126,8 @@ class UpdatesState
|
||||
$this->{$param}($init[$param]);
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get/set PTS.
|
||||
*
|
||||
@ -151,10 +140,8 @@ class UpdatesState
|
||||
if ($set !== 0 && $set > $this->pts) {
|
||||
$this->pts = $set;
|
||||
}
|
||||
|
||||
return $this->pts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get/set QTS.
|
||||
*
|
||||
@ -167,10 +154,8 @@ class UpdatesState
|
||||
if ($set !== 0 && $set > $this->qts) {
|
||||
$this->qts = $set;
|
||||
}
|
||||
|
||||
return $this->qts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get/set seq.
|
||||
*
|
||||
@ -183,10 +168,8 @@ class UpdatesState
|
||||
if ($set !== 0 && $set > $this->seq) {
|
||||
$this->seq = $set;
|
||||
}
|
||||
|
||||
return $this->seq;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get/set date.
|
||||
*
|
||||
@ -199,10 +182,8 @@ class UpdatesState
|
||||
if ($set !== 0 && $set > $this->date) {
|
||||
$this->date = $set;
|
||||
}
|
||||
|
||||
return $this->date;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check validity of PTS contained in update.
|
||||
*
|
||||
@ -214,7 +195,6 @@ class UpdatesState
|
||||
{
|
||||
return $update['pts'] - ($this->pts + $update['pts_count']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check validity of seq contained in update.
|
||||
*
|
||||
|
File diff suppressed because one or more lines are too long
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* MyTelegramOrgWrapper module.
|
||||
*
|
||||
@ -27,7 +28,6 @@ use Amp\Http\Client\Request;
|
||||
class MyTelegramOrgWrapper
|
||||
{
|
||||
use Tools;
|
||||
|
||||
private $logged = false;
|
||||
private $hash = '';
|
||||
private $number;
|
||||
@ -36,29 +36,25 @@ class MyTelegramOrgWrapper
|
||||
private $async = true;
|
||||
private $jar;
|
||||
const MY_TELEGRAM_URL = 'https://my.telegram.org';
|
||||
|
||||
public function __sleep()
|
||||
{
|
||||
return ['logged', 'hash', 'number', 'creation_hash', 'settings', 'async', 'jar'];
|
||||
}
|
||||
|
||||
public function __construct($settings = [])
|
||||
{
|
||||
$this->settings = MTProto::getSettings($settings, $this->settings);
|
||||
$this->__wakeup();
|
||||
}
|
||||
|
||||
public function __wakeup()
|
||||
{
|
||||
if ($this->settings === null) {
|
||||
$this->settings = [];
|
||||
}
|
||||
if (!$this->jar || !($this->jar instanceof InMemoryCookieJar)) {
|
||||
$this->jar = new InMemoryCookieJar;
|
||||
if (!$this->jar || !$this->jar instanceof InMemoryCookieJar) {
|
||||
$this->jar = new InMemoryCookieJar();
|
||||
}
|
||||
$this->settings = MTProto::getSettings($this->settings);
|
||||
$this->datacenter = new DataCenter(
|
||||
new class($this->settings) {
|
||||
$this->datacenter = new DataCenter(new class($this->settings) {
|
||||
public function __construct($settings)
|
||||
{
|
||||
$this->logger = Logger::getLoggerFromSettings($settings);
|
||||
@ -67,15 +63,9 @@ class MyTelegramOrgWrapper
|
||||
{
|
||||
return $this->logger;
|
||||
}
|
||||
},
|
||||
[],
|
||||
$this->settings['connection_settings'],
|
||||
true,
|
||||
$this->jar
|
||||
);
|
||||
}, [], $this->settings['connection_settings'], true, $this->jar);
|
||||
}
|
||||
|
||||
public function login($number)
|
||||
public function login($number): \Generator
|
||||
{
|
||||
$this->number = $number;
|
||||
$request = new Request(self::MY_TELEGRAM_URL . '/auth/send_password', 'POST');
|
||||
@ -84,27 +74,22 @@ class MyTelegramOrgWrapper
|
||||
$response = yield $this->datacenter->getHTTPClient()->request($request);
|
||||
$result = yield $response->getBody()->buffer();
|
||||
$resulta = \json_decode($result, true);
|
||||
|
||||
if (!isset($resulta['random_hash'])) {
|
||||
throw new Exception($result);
|
||||
}
|
||||
$this->hash = $resulta['random_hash'];
|
||||
}
|
||||
|
||||
public function completeLogin($password)
|
||||
public function completeLogin($password): \Generator
|
||||
{
|
||||
if ($this->logged) {
|
||||
throw new Exception('Already logged in!');
|
||||
}
|
||||
|
||||
$request = new Request(self::MY_TELEGRAM_URL . '/auth/login', 'POST');
|
||||
$request->setBody(\http_build_query(['phone' => $this->number, 'random_hash' => $this->hash, 'password' => $password]));
|
||||
$request->setHeaders($this->getHeaders('origin'));
|
||||
$request->setHeader('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()->buffer();
|
||||
|
||||
|
||||
switch ($result) {
|
||||
case 'true':
|
||||
//Logger::log(['Login OK'], Logger::VERBOSE);
|
||||
@ -112,52 +97,41 @@ class MyTelegramOrgWrapper
|
||||
default:
|
||||
throw new Exception($result);
|
||||
}
|
||||
|
||||
return $this->logged = true;
|
||||
}
|
||||
|
||||
public function loggedIn()
|
||||
{
|
||||
return $this->logged;
|
||||
}
|
||||
|
||||
public function hasApp()
|
||||
public function hasApp(): \Generator
|
||||
{
|
||||
if (!$this->logged) {
|
||||
throw new Exception('Not logged in!');
|
||||
}
|
||||
|
||||
$request = new Request(self::MY_TELEGRAM_URL . '/apps');
|
||||
$request->setHeaders($this->getHeaders('refer'));
|
||||
$response = yield $this->datacenter->getHTTPClient()->request($request);
|
||||
$result = yield $response->getBody()->buffer();
|
||||
|
||||
$title = \explode('</title>', \explode('<title>', $result)[1])[0];
|
||||
switch ($title) {
|
||||
case 'App configuration':
|
||||
return true;
|
||||
case 'Create new application':
|
||||
$this->creation_hash = \explode('"/>', \explode('<input type="hidden" name="hash" value="', $result)[1])[0];
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->logged = false;
|
||||
|
||||
throw new Exception($title);
|
||||
}
|
||||
|
||||
public function getApp()
|
||||
public function getApp(): \Generator
|
||||
{
|
||||
if (!$this->logged) {
|
||||
throw new Exception('Not logged in!');
|
||||
}
|
||||
|
||||
$request = new Request(self::MY_TELEGRAM_URL . '/apps');
|
||||
$request->setHeaders($this->getHeaders('refer'));
|
||||
$response = yield $this->datacenter->getHTTPClient()->request($request);
|
||||
$result = yield $response->getBody()->buffer();
|
||||
|
||||
$cose = \explode('<label for="app_id" class="col-md-4 text-right control-label">App api_id:</label>
|
||||
<div class="col-md-7">
|
||||
<span class="form-control input-xlarge uneditable-input" onclick="this.select();"><strong>', $result);
|
||||
@ -168,41 +142,33 @@ class MyTelegramOrgWrapper
|
||||
<span class="form-control input-xlarge uneditable-input" onclick="this.select();">', $result);
|
||||
$asd = \explode('</span>', $cose[1]);
|
||||
$api_hash = $asd[0];
|
||||
|
||||
return ['api_id' => (int) $api_id, 'api_hash' => $api_hash];
|
||||
}
|
||||
|
||||
public function createApp($settings)
|
||||
public function createApp($settings): \Generator
|
||||
{
|
||||
if (!$this->logged) {
|
||||
throw new Exception('Not logged in!');
|
||||
}
|
||||
if (yield $this->hasApp()) {
|
||||
if (yield from $this->hasApp()) {
|
||||
throw new Exception('The app was already created!');
|
||||
}
|
||||
|
||||
$request = new Request(self::MY_TELEGRAM_URL . '/apps/create', 'POST');
|
||||
$request->setHeaders($this->getHeaders('app'));
|
||||
$request->setBody(\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()->buffer();
|
||||
|
||||
if ($result) {
|
||||
throw new Exception(\html_entity_decode($result));
|
||||
}
|
||||
|
||||
$request = new Request(self::MY_TELEGRAM_URL . '/apps');
|
||||
$request->setHeaders($this->getHeaders('refer'));
|
||||
$response = yield $this->datacenter->getHTTPClient()->request($request);
|
||||
$result = yield $response->getBody()->buffer();
|
||||
|
||||
$title = \explode('</title>', \explode('<title>', $result)[1])[0];
|
||||
if ($title === 'Create new application') {
|
||||
$this->creation_hash = \explode('"/>', \explode('<input type="hidden" name="hash" value="', $result)[1])[0];
|
||||
|
||||
throw new \danog\MadelineProto\Exception('App creation failed');
|
||||
}
|
||||
|
||||
$cose = \explode('<label for="app_id" class="col-md-4 text-right control-label">App api_id:</label>
|
||||
<div class="col-md-7">
|
||||
<span class="form-control input-xlarge uneditable-input" onclick="this.select();"><strong>', $result);
|
||||
@ -213,10 +179,8 @@ class MyTelegramOrgWrapper
|
||||
<span class="form-control input-xlarge uneditable-input" onclick="this.select();">', $result);
|
||||
$asd = \explode('</span>', $cose['1']);
|
||||
$api_hash = $asd['0'];
|
||||
|
||||
return ['api_id' => (int) $api_id, 'api_hash' => $api_hash];
|
||||
}
|
||||
|
||||
/**
|
||||
* Function for generating curl request headers.
|
||||
*/
|
||||
@ -228,7 +192,6 @@ class MyTelegramOrgWrapper
|
||||
$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':
|
||||
@ -255,16 +218,13 @@ class MyTelegramOrgWrapper
|
||||
$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;
|
||||
}
|
||||
|
||||
public function async($async)
|
||||
{
|
||||
$this->async = $async;
|
||||
@ -277,9 +237,8 @@ class MyTelegramOrgWrapper
|
||||
{
|
||||
$name .= '_async';
|
||||
$async = \is_array(\end($arguments)) && isset(\end($arguments)['async']) ? \end($arguments)['async'] : $this->async;
|
||||
|
||||
if (!\method_exists($this, $name)) {
|
||||
throw new Exception("$name does not exist!");
|
||||
throw new Exception("{$name} does not exist!");
|
||||
}
|
||||
return $async ? $this->{$name}(...$arguments) : Tools::wait($this->{$name}(...$arguments));
|
||||
}
|
||||
|
@ -22,12 +22,10 @@ namespace danog\MadelineProto;
|
||||
class PTSException extends \Exception
|
||||
{
|
||||
use TL\PrettyException;
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
return \get_class($this) . ($this->message !== '' ? ': ' : '') . $this->message . PHP_EOL . 'TL Trace:' . PHP_EOL . PHP_EOL . $this->getTLTrace() . PHP_EOL;
|
||||
}
|
||||
|
||||
public function __construct($message, $file = '')
|
||||
{
|
||||
parent::__construct($message);
|
||||
|
@ -0,0 +1 @@
|
||||
<?php
|
@ -24,101 +24,43 @@ class RPCErrorException extends \Exception
|
||||
use TL\PrettyException;
|
||||
private $fetched = false;
|
||||
public static $rollbar = true;
|
||||
|
||||
public static $descriptions = [
|
||||
'RPC_MCGET_FAIL' => 'Telegram is having internal issues, please try again later.',
|
||||
'RPC_CALL_FAIL' => 'Telegram is having internal issues, please try again later.',
|
||||
'USER_PRIVACY_RESTRICTED' => "The user's privacy settings do not allow you to do this",
|
||||
'CHANNEL_PRIVATE' => "You haven't joined this channel/supergroup",
|
||||
'USER_IS_BOT' => "Bots can't send messages to other bots",
|
||||
'BOT_METHOD_INVALID' => 'This method cannot be run by a bot',
|
||||
'PHONE_CODE_EXPIRED' => 'The phone code you provided has expired, this may happen if it was sent to any chat on telegram (if the code is sent through a telegram chat (not the official account) to avoid it append or prepend to the code some chars)',
|
||||
'USERNAME_INVALID' => 'The provided username is not valid',
|
||||
'ACCESS_TOKEN_INVALID' => 'The provided token is not valid',
|
||||
'ACTIVE_USER_REQUIRED' => 'The method is only available to already activated users',
|
||||
'FIRSTNAME_INVALID' => 'The first name is invalid',
|
||||
'LASTNAME_INVALID' => 'The last name is invalid',
|
||||
'PHONE_NUMBER_INVALID' => 'The phone number is invalid',
|
||||
'PHONE_CODE_HASH_EMPTY' => 'phone_code_hash is missing',
|
||||
'PHONE_CODE_EMPTY' => 'phone_code is missing',
|
||||
'PHONE_CODE_EXPIRED' => 'The confirmation code has expired',
|
||||
'API_ID_INVALID' => 'The api_id/api_hash combination is invalid',
|
||||
'PHONE_NUMBER_OCCUPIED' => 'The phone number is already in use',
|
||||
'PHONE_NUMBER_UNOCCUPIED' => 'The phone number is not yet being used',
|
||||
'USERS_TOO_FEW' => 'Not enough users (to create a chat, for example)',
|
||||
'USERS_TOO_MUCH' => 'The maximum number of users has been exceeded (to create a chat, for example)',
|
||||
'TYPE_CONSTRUCTOR_INVALID' => 'The type constructor is invalid',
|
||||
'FILE_PART_INVALID' => 'The file part number is invalid',
|
||||
'FILE_PARTS_INVALID' => 'The number of file parts is invalid',
|
||||
'MD5_CHECKSUM_INVALID' => 'The MD5 checksums do not match',
|
||||
'PHOTO_INVALID_DIMENSIONS' => 'The photo dimensions are invalid',
|
||||
'FIELD_NAME_INVALID' => 'The field with the name FIELD_NAME is invalid',
|
||||
'FIELD_NAME_EMPTY' => 'The field with the name FIELD_NAME is missing',
|
||||
'MSG_WAIT_FAILED' => 'A waiting call returned an error',
|
||||
'USERNAME_NOT_OCCUPIED' => 'The provided username is not occupied',
|
||||
'PHONE_NUMBER_BANNED' => 'The provided phone number is banned from telegram',
|
||||
'AUTH_KEY_UNREGISTERED' => 'The authorization key has expired',
|
||||
'INVITE_HASH_EXPIRED' => 'The invite link has expired',
|
||||
'USER_DEACTIVATED' => 'The user was deactivated',
|
||||
'USER_ALREADY_PARTICIPANT' => 'The user is already in the group',
|
||||
'MESSAGE_ID_INVALID' => 'The provided message id is invalid',
|
||||
'PEER_ID_INVALID' => 'The provided peer id is invalid',
|
||||
'CHAT_ID_INVALID' => 'The provided chat id is invalid',
|
||||
'MESSAGE_DELETE_FORBIDDEN' => "You can't delete one of the messages you tried to delete, most likely because it is a service message.",
|
||||
'CHAT_ADMIN_REQUIRED' => 'You must be an admin in this chat to do this',
|
||||
-429 => 'Too many requests',
|
||||
'PEER_FLOOD' => "You are spamreported, you can't do this",
|
||||
];
|
||||
public static $descriptions = ['RPC_MCGET_FAIL' => 'Telegram is having internal issues, please try again later.', 'RPC_CALL_FAIL' => 'Telegram is having internal issues, please try again later.', 'USER_PRIVACY_RESTRICTED' => "The user's privacy settings do not allow you to do this", 'CHANNEL_PRIVATE' => "You haven't joined this channel/supergroup", 'USER_IS_BOT' => "Bots can't send messages to other bots", 'BOT_METHOD_INVALID' => 'This method cannot be run by a bot', 'PHONE_CODE_EXPIRED' => 'The phone code you provided has expired, this may happen if it was sent to any chat on telegram (if the code is sent through a telegram chat (not the official account) to avoid it append or prepend to the code some chars)', 'USERNAME_INVALID' => 'The provided username is not valid', 'ACCESS_TOKEN_INVALID' => 'The provided token is not valid', 'ACTIVE_USER_REQUIRED' => 'The method is only available to already activated users', 'FIRSTNAME_INVALID' => 'The first name is invalid', 'LASTNAME_INVALID' => 'The last name is invalid', 'PHONE_NUMBER_INVALID' => 'The phone number is invalid', 'PHONE_CODE_HASH_EMPTY' => 'phone_code_hash is missing', 'PHONE_CODE_EMPTY' => 'phone_code is missing', 'PHONE_CODE_EXPIRED' => 'The confirmation code has expired', 'API_ID_INVALID' => 'The api_id/api_hash combination is invalid', 'PHONE_NUMBER_OCCUPIED' => 'The phone number is already in use', 'PHONE_NUMBER_UNOCCUPIED' => 'The phone number is not yet being used', 'USERS_TOO_FEW' => 'Not enough users (to create a chat, for example)', 'USERS_TOO_MUCH' => 'The maximum number of users has been exceeded (to create a chat, for example)', 'TYPE_CONSTRUCTOR_INVALID' => 'The type constructor is invalid', 'FILE_PART_INVALID' => 'The file part number is invalid', 'FILE_PARTS_INVALID' => 'The number of file parts is invalid', 'MD5_CHECKSUM_INVALID' => 'The MD5 checksums do not match', 'PHOTO_INVALID_DIMENSIONS' => 'The photo dimensions are invalid', 'FIELD_NAME_INVALID' => 'The field with the name FIELD_NAME is invalid', 'FIELD_NAME_EMPTY' => 'The field with the name FIELD_NAME is missing', 'MSG_WAIT_FAILED' => 'A waiting call returned an error', 'USERNAME_NOT_OCCUPIED' => 'The provided username is not occupied', 'PHONE_NUMBER_BANNED' => 'The provided phone number is banned from telegram', 'AUTH_KEY_UNREGISTERED' => 'The authorization key has expired', 'INVITE_HASH_EXPIRED' => 'The invite link has expired', 'USER_DEACTIVATED' => 'The user was deactivated', 'USER_ALREADY_PARTICIPANT' => 'The user is already in the group', 'MESSAGE_ID_INVALID' => 'The provided message id is invalid', 'PEER_ID_INVALID' => 'The provided peer id is invalid', 'CHAT_ID_INVALID' => 'The provided chat id is invalid', 'MESSAGE_DELETE_FORBIDDEN' => "You can't delete one of the messages you tried to delete, most likely because it is a service message.", 'CHAT_ADMIN_REQUIRED' => 'You must be an admin in this chat to do this', -429 => 'Too many requests', 'PEER_FLOOD' => "You are spamreported, you can't do this"];
|
||||
public static $errorMethodMap = [];
|
||||
|
||||
private $caller = '';
|
||||
public static function localizeMessage($method, $code, $error)
|
||||
{
|
||||
if (!$method || !$code || !$error) {
|
||||
return $error;
|
||||
}
|
||||
|
||||
$error = \preg_replace('/\d+$/', "X", $error);
|
||||
|
||||
$error = \preg_replace('/\\d+$/', "X", $error);
|
||||
$description = self::$descriptions[$error] ?? '';
|
||||
|
||||
|
||||
if (!isset(self::$errorMethodMap[$code][$method][$error])
|
||||
|| !isset(self::$descriptions[$error])
|
||||
|| $code === 500
|
||||
) {
|
||||
if (!isset(self::$errorMethodMap[$code][$method][$error]) || !isset(self::$descriptions[$error]) || $code === 500) {
|
||||
$res = \json_decode(@\file_get_contents('https://rpc.pwrtelegram.xyz/?method=' . $method . '&code=' . $code . '&error=' . $error, false, \stream_context_create(['http' => ['timeout' => 3]])), true);
|
||||
if (isset($res['ok']) && $res['ok'] && isset($res['result'])) {
|
||||
$description = $res['result'];
|
||||
|
||||
self::$descriptions[$error] = $description;
|
||||
self::$errorMethodMap[$code][$method][$error] = $error;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$description) {
|
||||
return $error;
|
||||
}
|
||||
return $description;
|
||||
}
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
$result = \sprintf(\danog\MadelineProto\Lang::$current_lang['rpc_tg_error'], self::localizeMessage($this->caller, $this->code, $this->message) . " ({$this->code})", $this->rpc, $this->file, $this->line . PHP_EOL, \danog\MadelineProto\Magic::$revision . PHP_EOL . PHP_EOL) . PHP_EOL . $this->getTLTrace() . PHP_EOL;
|
||||
if (PHP_SAPI !== 'cli') {
|
||||
$result = \str_replace(PHP_EOL, '<br>' . PHP_EOL, $result);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function __construct($message = null, $code = 0, $caller = '', Exception $previous = null)
|
||||
{
|
||||
$this->rpc = $message;
|
||||
parent::__construct($message, $code, $previous);
|
||||
$this->prettifyTL($caller);
|
||||
$this->caller = $caller;
|
||||
|
||||
$additional = [];
|
||||
foreach ($this->getTrace() as $level) {
|
||||
if (isset($level['function']) && $level['function'] === 'methodCall') {
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* RSA module.
|
||||
*
|
||||
@ -45,7 +46,6 @@ class RSA
|
||||
* @var string
|
||||
*/
|
||||
public $fp;
|
||||
|
||||
/**
|
||||
* Load RSA key.
|
||||
*
|
||||
@ -62,11 +62,9 @@ class RSA
|
||||
$this->n = Tools::getVar($key, 'modulus');
|
||||
$this->e = Tools::getVar($key, 'exponent');
|
||||
\danog\MadelineProto\Logger::log(\danog\MadelineProto\Lang::$current_lang['computing_fingerprint'], Logger::ULTRA_VERBOSE);
|
||||
$this->fp = \substr(\sha1((yield $TL->serializeObject(['type' => 'bytes'], $this->n->toBytes(), 'key')).(yield $TL->serializeObject(['type' => 'bytes'], $this->e->toBytes(), 'key')), true), -8);
|
||||
|
||||
$this->fp = \substr(\sha1(yield $TL->serializeObject(['type' => 'bytes'], $this->n->toBytes(), 'key') . yield $TL->serializeObject(['type' => 'bytes'], $this->e->toBytes(), 'key'), true), -8);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sleep function.
|
||||
*
|
||||
@ -76,7 +74,6 @@ class RSA
|
||||
{
|
||||
return ['e', 'n', 'fp'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypt data.
|
||||
*
|
||||
@ -87,7 +84,6 @@ class RSA
|
||||
public function encrypt($data): string
|
||||
{
|
||||
\danog\MadelineProto\Logger::log(\danog\MadelineProto\Lang::$current_lang['rsa_encrypting'], Logger::VERBOSE);
|
||||
|
||||
return (new \tgseclib\Math\BigInteger((string) $data, 256))->powMod($this->e, $this->n)->toBytes();
|
||||
}
|
||||
}
|
||||
|
@ -40,7 +40,6 @@ trait AuthKeyHandler
|
||||
* @var array
|
||||
*/
|
||||
protected $secret_chats = [];
|
||||
|
||||
/**
|
||||
* Accept secret chat.
|
||||
*
|
||||
@ -54,7 +53,6 @@ trait AuthKeyHandler
|
||||
if ($this->secretChatStatus($params['id']) !== 0) {
|
||||
//$this->logger->logger($this->secretChatStatus($params['id']));
|
||||
$this->logger->logger("I've already accepted secret chat " . $params['id']);
|
||||
|
||||
return false;
|
||||
}
|
||||
$dh_config = yield $this->getDhConfig();
|
||||
@ -71,10 +69,9 @@ trait AuthKeyHandler
|
||||
$g_b = $dh_config['g']->powMod($b, $dh_config['p']);
|
||||
$this->checkG($g_b, $dh_config['p']);
|
||||
yield $this->methodCallAsyncRead('messages.acceptEncryption', ['peer' => $params['id'], 'g_b' => $g_b->toBytes(), 'key_fingerprint' => $key['fingerprint']], ['datacenter' => $this->datacenter->curdc]);
|
||||
yield $this->notifyLayer($params['id']);
|
||||
yield from $this->notifyLayer($params['id']);
|
||||
$this->logger->logger('Secret chat ' . $params['id'] . ' accepted successfully!', \danog\MadelineProto\Logger::NOTICE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Request secret chat.
|
||||
*
|
||||
@ -100,10 +97,8 @@ trait AuthKeyHandler
|
||||
$this->temp_requested_secret_chats[$res['id']] = $a;
|
||||
$this->updaters[false]->resume();
|
||||
$this->logger->logger('Secret chat ' . $res['id'] . ' requested successfully!', \danog\MadelineProto\Logger::NOTICE);
|
||||
|
||||
return $res['id'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Complete secret chat.
|
||||
*
|
||||
@ -116,7 +111,6 @@ trait AuthKeyHandler
|
||||
if ($this->secretChatStatus($params['id']) !== 1) {
|
||||
//$this->logger->logger($this->secretChatStatus($params['id']));
|
||||
$this->logger->logger('Could not find and complete secret chat ' . $params['id']);
|
||||
|
||||
return false;
|
||||
}
|
||||
$dh_config = yield $this->getDhConfig();
|
||||
@ -127,29 +121,25 @@ trait AuthKeyHandler
|
||||
$key['fingerprint'] = \substr(\sha1($key['auth_key'], true), -8);
|
||||
//$this->logger->logger($key);
|
||||
if ($key['fingerprint'] !== $params['key_fingerprint']) {
|
||||
yield $this->discardSecretChat($params['id']);
|
||||
|
||||
yield from $this->discardSecretChat($params['id']);
|
||||
throw new \danog\MadelineProto\SecurityException('Invalid key fingerprint!');
|
||||
}
|
||||
$key['visualization_orig'] = \substr(\sha1($key['auth_key'], true), 16);
|
||||
$key['visualization_46'] = \substr(\hash('sha256', $key['auth_key'], true), 20);
|
||||
$this->secret_chats[$params['id']] = ['key' => $key, 'admin' => true, 'user_id' => $params['participant_id'], 'InputEncryptedChat' => ['chat_id' => $params['id'], 'access_hash' => $params['access_hash'], '_' => 'inputEncryptedChat'], 'in_seq_no_x' => 0, 'out_seq_no_x' => 1, 'in_seq_no' => 0, 'out_seq_no' => 0, 'layer' => 8, 'ttl' => 0, 'ttr' => 100, 'updated' => \time(), 'incoming' => [], 'outgoing' => [], 'created' => \time(), 'rekeying' => [0], 'key_x' => 'to server', 'mtproto' => 1];
|
||||
yield $this->notifyLayer($params['id']);
|
||||
yield from $this->notifyLayer($params['id']);
|
||||
$this->logger->logger('Secret chat ' . $params['id'] . ' completed successfully!', \danog\MadelineProto\Logger::NOTICE);
|
||||
}
|
||||
|
||||
private function notifyLayer($chat): \Generator
|
||||
{
|
||||
yield $this->methodCallAsyncRead('messages.sendEncryptedService', ['peer' => $chat, 'message' => ['_' => 'decryptedMessageService', 'action' => ['_' => 'decryptedMessageActionNotifyLayer', 'layer' => $this->TL->getSecretLayer()]]], ['datacenter' => $this->datacenter->curdc]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Temporary rekeyed secret chats.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $temp_rekeyed_secret_chats = [];
|
||||
|
||||
/**
|
||||
* Rekey secret chat.
|
||||
*
|
||||
@ -174,10 +164,8 @@ trait AuthKeyHandler
|
||||
$this->secret_chats[$chat]['rekeying'] = [1, $e];
|
||||
yield $this->methodCallAsyncRead('messages.sendEncryptedService', ['peer' => $chat, 'message' => ['_' => 'decryptedMessageService', 'action' => ['_' => 'decryptedMessageActionRequestKey', 'g_a' => $g_a->toBytes(), 'exchange_id' => $e]]], ['datacenter' => $this->datacenter->curdc]);
|
||||
$this->updaters[false]->resume();
|
||||
|
||||
return $e;
|
||||
}
|
||||
|
||||
/**
|
||||
* Accept rekeying.
|
||||
*
|
||||
@ -197,7 +185,6 @@ trait AuthKeyHandler
|
||||
}
|
||||
if ($my_exchange_id->compare($other_exchange_id) === 0) {
|
||||
$this->secret_chats[$chat]['rekeying'] = [0];
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -218,7 +205,6 @@ trait AuthKeyHandler
|
||||
yield $this->methodCallAsyncRead('messages.sendEncryptedService', ['peer' => $chat, 'message' => ['_' => 'decryptedMessageService', 'action' => ['_' => 'decryptedMessageActionAcceptKey', 'g_b' => $g_b->toBytes(), 'exchange_id' => $params['exchange_id'], 'key_fingerprint' => $key['fingerprint']]]], ['datacenter' => $this->datacenter->curdc]);
|
||||
$this->updaters[false]->resume();
|
||||
}
|
||||
|
||||
/**
|
||||
* Commit rekeying of secret chat.
|
||||
*
|
||||
@ -231,7 +217,6 @@ trait AuthKeyHandler
|
||||
{
|
||||
if ($this->secret_chats[$chat]['rekeying'][0] !== 1 || !isset($this->temp_rekeyed_secret_chats[$params['exchange_id']])) {
|
||||
$this->secret_chats[$chat]['rekeying'] = [0];
|
||||
|
||||
return;
|
||||
}
|
||||
$this->logger->logger('Committing rekeying of secret chat ' . $chat . '...', \danog\MadelineProto\Logger::VERBOSE);
|
||||
@ -244,7 +229,6 @@ trait AuthKeyHandler
|
||||
$key['visualization_46'] = \substr(\hash('sha256', $key['auth_key'], true), 20);
|
||||
if ($key['fingerprint'] !== $params['key_fingerprint']) {
|
||||
yield $this->methodCallAsyncRead('messages.sendEncryptedService', ['peer' => $chat, 'message' => ['_' => 'decryptedMessageService', 'action' => ['_' => 'decryptedMessageActionAbortKey', 'exchange_id' => $params['exchange_id']]]], ['datacenter' => $this->datacenter->curdc]);
|
||||
|
||||
throw new \danog\MadelineProto\SecurityException('Invalid key fingerprint!');
|
||||
}
|
||||
yield $this->methodCallAsyncRead('messages.sendEncryptedService', ['peer' => $chat, 'message' => ['_' => 'decryptedMessageService', 'action' => ['_' => 'decryptedMessageActionCommitKey', 'exchange_id' => $params['exchange_id'], 'key_fingerprint' => $key['fingerprint']]]], ['datacenter' => $this->datacenter->curdc]);
|
||||
@ -256,7 +240,6 @@ trait AuthKeyHandler
|
||||
$this->secret_chats[$chat]['updated'] = \time();
|
||||
$this->updaters[false]->resume();
|
||||
}
|
||||
|
||||
/**
|
||||
* Complete rekeying.
|
||||
*
|
||||
@ -272,7 +255,6 @@ trait AuthKeyHandler
|
||||
}
|
||||
if ($this->temp_rekeyed_secret_chats[$params['exchange_id']]['fingerprint'] !== $params['key_fingerprint']) {
|
||||
yield $this->methodCallAsyncRead('messages.sendEncryptedService', ['peer' => $chat, 'message' => ['_' => 'decryptedMessageService', 'action' => ['_' => 'decryptedMessageActionAbortKey', 'exchange_id' => $params['exchange_id']]]], ['datacenter' => $this->datacenter->curdc]);
|
||||
|
||||
throw new \danog\MadelineProto\SecurityException('Invalid key fingerprint!');
|
||||
}
|
||||
$this->logger->logger('Completing rekeying of secret chat ' . $chat . '...', \danog\MadelineProto\Logger::VERBOSE);
|
||||
@ -284,10 +266,8 @@ trait AuthKeyHandler
|
||||
unset($this->temp_rekeyed_secret_chats[$params['exchange_id']]);
|
||||
yield $this->methodCallAsyncRead('messages.sendEncryptedService', ['peer' => $chat, 'message' => ['_' => 'decryptedMessageService', 'action' => ['_' => 'decryptedMessageActionNoop']]], ['datacenter' => $this->datacenter->curdc]);
|
||||
$this->logger->logger('Secret chat ' . $chat . ' rekeyed successfully!', \danog\MadelineProto\Logger::VERBOSE);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get secret chat status.
|
||||
*
|
||||
@ -303,10 +283,8 @@ trait AuthKeyHandler
|
||||
if (isset($this->temp_requested_secret_chats[$chat])) {
|
||||
return MTProto::SECRET_REQUESTED;
|
||||
}
|
||||
|
||||
return MTProto::SECRET_EMPTY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get secret chat.
|
||||
*
|
||||
@ -318,7 +296,6 @@ trait AuthKeyHandler
|
||||
{
|
||||
return $this->secret_chats[\is_array($chat) ? $chat['chat_id'] : $chat];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether secret chat exists.
|
||||
*
|
||||
@ -330,7 +307,6 @@ trait AuthKeyHandler
|
||||
{
|
||||
return isset($this->secret_chats[\is_array($chat) ? $chat['chat_id'] : $chat]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Discard secret chat.
|
||||
*
|
||||
@ -347,8 +323,6 @@ trait AuthKeyHandler
|
||||
if (isset($this->temp_requested_secret_chats[$chat])) {
|
||||
unset($this->temp_requested_secret_chats[$chat]);
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
yield $this->methodCallAsyncRead('messages.discardEncryption', ['chat_id' => $chat], ['datacenter' => $this->datacenter->curdc]);
|
||||
} catch (\danog\MadelineProto\RPCErrorException $e) {
|
||||
|
@ -38,7 +38,6 @@ trait MessageHandler
|
||||
{
|
||||
if (!isset($this->secret_chats[$chat_id])) {
|
||||
$this->logger->logger(\sprintf(\danog\MadelineProto\Lang::$current_lang['secret_chat_skipping'], $chat_id));
|
||||
|
||||
return false;
|
||||
}
|
||||
$message['random_id'] = \danog\MadelineProto\Tools::random(8);
|
||||
@ -67,15 +66,12 @@ trait MessageHandler
|
||||
$message .= \danog\MadelineProto\Tools::random(\danog\MadelineProto\Tools::posmod(-\strlen($message), 16));
|
||||
}
|
||||
$message = $this->secret_chats[$chat_id]['key']['fingerprint'] . $message_key . $this->igeEncrypt($message, $aes_key, $aes_iv);
|
||||
|
||||
return $message;
|
||||
}
|
||||
|
||||
private function handleEncryptedUpdate(array $message): \Generator
|
||||
{
|
||||
if (!isset($this->secret_chats[$message['message']['chat_id']])) {
|
||||
$this->logger->logger(\sprintf(\danog\MadelineProto\Lang::$current_lang['secret_chat_skipping'], $message['message']['chat_id']));
|
||||
|
||||
return false;
|
||||
}
|
||||
$auth_key_id = \substr($message['message']['bytes'], 0, 8);
|
||||
@ -84,13 +80,11 @@ trait MessageHandler
|
||||
if (isset($this->secret_chats[$message['message']['chat_id']]['old_key']['fingerprint'])) {
|
||||
if ($auth_key_id !== $this->secret_chats[$message['message']['chat_id']]['old_key']['fingerprint']) {
|
||||
yield $this->discardSecretChat($message['message']['chat_id']);
|
||||
|
||||
throw new \danog\MadelineProto\SecurityException(\danog\MadelineProto\Lang::$current_lang['fingerprint_mismatch']);
|
||||
}
|
||||
$old = true;
|
||||
} else {
|
||||
yield $this->discardSecretChat($message['message']['chat_id']);
|
||||
|
||||
throw new \danog\MadelineProto\SecurityException(\danog\MadelineProto\Lang::$current_lang['fingerprint_mismatch']);
|
||||
}
|
||||
}
|
||||
@ -98,7 +92,6 @@ trait MessageHandler
|
||||
$encrypted_data = \substr($message['message']['bytes'], 24);
|
||||
if ($this->secret_chats[$message['message']['chat_id']]['mtproto'] === 2) {
|
||||
$this->logger->logger('Trying MTProto v2 decryption for chat ' . $message['message']['chat_id'] . '...', \danog\MadelineProto\Logger::NOTICE);
|
||||
|
||||
try {
|
||||
$message_data = $this->tryMTProtoV2Decrypt($message_key, $message['message']['chat_id'], $old, $encrypted_data);
|
||||
$this->logger->logger('MTProto v2 decryption OK for chat ' . $message['message']['chat_id'] . '...', \danog\MadelineProto\Logger::NOTICE);
|
||||
@ -110,7 +103,6 @@ trait MessageHandler
|
||||
}
|
||||
} else {
|
||||
$this->logger->logger('Trying MTProto v1 decryption for chat ' . $message['message']['chat_id'] . '...', \danog\MadelineProto\Logger::NOTICE);
|
||||
|
||||
try {
|
||||
$message_data = $this->tryMTProtoV1Decrypt($message_key, $message['message']['chat_id'], $old, $encrypted_data);
|
||||
$this->logger->logger('MTProto v1 decryption OK for chat ' . $message['message']['chat_id'] . '...', \danog\MadelineProto\Logger::NOTICE);
|
||||
@ -131,7 +123,6 @@ trait MessageHandler
|
||||
$this->secret_chats[$message['message']['chat_id']]['incoming'][$this->secret_chats[$message['message']['chat_id']]['in_seq_no']] = $message['message'];
|
||||
yield $this->handleDecryptedUpdate($message);
|
||||
}
|
||||
|
||||
private function tryMTProtoV1Decrypt($message_key, $chat_id, $old, $encrypted_data)
|
||||
{
|
||||
list($aes_key, $aes_iv) = $this->oldAesCalculate($message_key, $this->secret_chats[$chat_id][$old ? 'old_key' : 'key']['auth_key'], true);
|
||||
@ -150,10 +141,8 @@ trait MessageHandler
|
||||
if (\strlen($decrypted_data) % 16 != 0) {
|
||||
throw new \danog\MadelineProto\SecurityException(\danog\MadelineProto\Lang::$current_lang['length_not_divisible_16']);
|
||||
}
|
||||
|
||||
return $message_data;
|
||||
}
|
||||
|
||||
private function tryMTProtoV2Decrypt($message_key, $chat_id, $old, $encrypted_data)
|
||||
{
|
||||
list($aes_key, $aes_iv) = $this->aesCalculate($message_key, $this->secret_chats[$chat_id][$old ? 'old_key' : 'key']['auth_key'], !$this->secret_chats[$chat_id]['admin']);
|
||||
@ -175,7 +164,6 @@ trait MessageHandler
|
||||
if (\strlen($decrypted_data) % 16 != 0) {
|
||||
throw new \danog\MadelineProto\SecurityException(\danog\MadelineProto\Lang::$current_lang['length_not_divisible_16']);
|
||||
}
|
||||
|
||||
return $message_data;
|
||||
}
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ namespace danog\MadelineProto\SecretChats;
|
||||
*/
|
||||
trait ResponseHandler
|
||||
{
|
||||
private function handleDecryptedUpdate($update)
|
||||
private function handleDecryptedUpdate($update): \Generator
|
||||
{
|
||||
/*if (isset($update['message']['decrypted_message']['random_bytes']) && strlen($update['message']['decrypted_message']['random_bytes']) < 15) {
|
||||
throw new \danog\MadelineProto\ResponseException(\danog\MadelineProto\Lang::$current_lang['rand_bytes_too_short']);
|
||||
@ -35,15 +35,12 @@ trait ResponseHandler
|
||||
switch ($update['message']['decrypted_message']['action']['_']) {
|
||||
case 'decryptedMessageActionRequestKey':
|
||||
yield $this->acceptRekey($update['message']['chat_id'], $update['message']['decrypted_message']['action']);
|
||||
|
||||
return;
|
||||
case 'decryptedMessageActionAcceptKey':
|
||||
yield $this->commitRekey($update['message']['chat_id'], $update['message']['decrypted_message']['action']);
|
||||
|
||||
return;
|
||||
case 'decryptedMessageActionCommitKey':
|
||||
yield $this->completeRekey($update['message']['chat_id'], $update['message']['decrypted_message']['action']);
|
||||
|
||||
return;
|
||||
case 'decryptedMessageActionNotifyLayer':
|
||||
$this->secret_chats[$update['message']['chat_id']]['layer'] = $update['message']['decrypted_message']['action']['layer'];
|
||||
@ -53,13 +50,10 @@ trait ResponseHandler
|
||||
if ($update['message']['decrypted_message']['action']['layer'] >= 73) {
|
||||
$this->secret_chats[$update['message']['chat_id']]['mtproto'] = 2;
|
||||
}
|
||||
|
||||
return;
|
||||
case 'decryptedMessageActionSetMessageTTL':
|
||||
$this->secret_chats[$update['message']['chat_id']]['ttl'] = $update['message']['decrypted_message']['action']['ttl_seconds'];
|
||||
|
||||
yield $this->saveUpdate($update);
|
||||
|
||||
return;
|
||||
case 'decryptedMessageActionNoop':
|
||||
return;
|
||||
@ -75,7 +69,6 @@ trait ResponseHandler
|
||||
yield $this->methodCallAsyncRead('messages.sendEncrypted', ['peer' => $update['message']['chat_id'], 'message' => $update['message']['decrypted_message']], ['datacenter' => $this->datacenter->curdc]);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
default:
|
||||
// yield $this->saveUpdate(['_' => 'updateNewDecryptedMessage', 'peer' => $this->secret_chats[$update['message']['chat_id']]['InputEncryptedChat'], 'in_seq_no' => $this->get_in_seq_no($update['message']['chat_id']), 'out_seq_no' => $this->get_out_seq_no($update['message']['chat_id']), 'message' => $update['message']['decrypted_message']]);
|
||||
@ -95,7 +88,7 @@ trait ResponseHandler
|
||||
}
|
||||
}
|
||||
$update['message']['decrypted_message'] = $update['message']['decrypted_message']['message'];
|
||||
yield $this->handleDecryptedUpdate($update);
|
||||
yield from $this->handleDecryptedUpdate($update);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
@ -24,7 +24,7 @@ namespace danog\MadelineProto\SecretChats;
|
||||
*/
|
||||
trait SeqNoHandler
|
||||
{
|
||||
private function checkSecretInSeqNo($chat_id, $seqno)
|
||||
private function checkSecretInSeqNo($chat_id, $seqno): \Generator
|
||||
{
|
||||
$seqno = ($seqno - $this->secret_chats[$chat_id]['out_seq_no_x']) / 2;
|
||||
$last = 0;
|
||||
@ -32,7 +32,6 @@ trait SeqNoHandler
|
||||
if (isset($message['decrypted_message']['in_seq_no'])) {
|
||||
if (($message['decrypted_message']['in_seq_no'] - $this->secret_chats[$chat_id]['out_seq_no_x']) / 2 < $last) {
|
||||
yield $this->discardSecretChat($chat_id);
|
||||
|
||||
throw new \danog\MadelineProto\SecurityException('in_seq_no is not increasing');
|
||||
}
|
||||
$last = ($message['decrypted_message']['in_seq_no'] - $this->secret_chats[$chat_id]['out_seq_no_x']) / 2;
|
||||
@ -40,14 +39,11 @@ trait SeqNoHandler
|
||||
}
|
||||
if ($seqno > $this->secret_chats[$chat_id]['out_seq_no'] + 1) {
|
||||
yield $this->discardSecretChat($chat_id);
|
||||
|
||||
throw new \danog\MadelineProto\SecurityException('in_seq_no is too big');
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function checkSecretOutSeqNo($chat_id, $seqno)
|
||||
private function checkSecretOutSeqNo($chat_id, $seqno): \Generator
|
||||
{
|
||||
$seqno = ($seqno - $this->secret_chats[$chat_id]['in_seq_no_x']) / 2;
|
||||
$C = 0;
|
||||
@ -55,7 +51,6 @@ trait SeqNoHandler
|
||||
if (isset($message['decrypted_message']['out_seq_no']) && $C < $this->secret_chats[$chat_id]['in_seq_no']) {
|
||||
if (($message['decrypted_message']['out_seq_no'] - $this->secret_chats[$chat_id]['in_seq_no_x']) / 2 !== $C) {
|
||||
yield $this->discardSecretChat($chat_id);
|
||||
|
||||
throw new \danog\MadelineProto\SecurityException('out_seq_no hole: should be ' . $C . ', is ' . ($message['decrypted_message']['out_seq_no'] - $this->secret_chats[$chat_id]['in_seq_no_x']) / 2);
|
||||
}
|
||||
$C++;
|
||||
@ -65,24 +60,19 @@ trait SeqNoHandler
|
||||
if ($seqno < $C) {
|
||||
// <= C
|
||||
$this->logger->logger('WARNING: dropping repeated message with seqno ' . $seqno);
|
||||
|
||||
return false;
|
||||
}
|
||||
if ($seqno > $C) {
|
||||
// > C+1
|
||||
yield $this->discardSecretChat($chat_id);
|
||||
|
||||
throw new \danog\MadelineProto\SecurityException('WARNING: out_seq_no gap detected (' . $seqno . ' > ' . $C . ')!');
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function generateSecretInSeqNo($chat)
|
||||
{
|
||||
return $this->secret_chats[$chat]['layer'] > 8 ? $this->secret_chats[$chat]['in_seq_no'] * 2 + $this->secret_chats[$chat]['in_seq_no_x'] : -1;
|
||||
}
|
||||
|
||||
private function generateSecretOutSeqNo($chat)
|
||||
{
|
||||
return $this->secret_chats[$chat]['layer'] > 8 ? $this->secret_chats[$chat]['out_seq_no'] * 2 + $this->secret_chats[$chat]['out_seq_no_x'] : -1;
|
||||
|
@ -27,7 +27,6 @@ class Serialization
|
||||
public static function realpaths($file)
|
||||
{
|
||||
$file = Absolute::absolute($file);
|
||||
|
||||
return ['file' => $file, 'lockfile' => $file . '.lock', 'tempfile' => $file . '.temp.session'];
|
||||
}
|
||||
}
|
||||
|
@ -27,41 +27,34 @@ class Server
|
||||
private $settings;
|
||||
private $pids = [];
|
||||
private $mypid;
|
||||
|
||||
public function __construct($settings)
|
||||
{
|
||||
\set_error_handler(['\\danog\\MadelineProto\\Exception', 'ExceptionErrorHandler']);
|
||||
\danog\MadelineProto\Logger::constructor(3);
|
||||
|
||||
if (!\extension_loaded('sockets')) {
|
||||
throw new Exception(['extension', 'sockets']);
|
||||
}
|
||||
|
||||
if (!\extension_loaded('pcntl')) {
|
||||
throw new Exception(['extension', 'pcntl']);
|
||||
}
|
||||
$this->settings = $settings;
|
||||
$this->mypid = \getmypid();
|
||||
}
|
||||
|
||||
public function start()
|
||||
{
|
||||
\pcntl_signal(SIGTERM, [$this, 'sigHandler']);
|
||||
\pcntl_signal(SIGINT, [$this, 'sigHandler']);
|
||||
\pcntl_signal(SIGCHLD, [$this, 'sigHandler']);
|
||||
|
||||
$this->sock = new \Socket($this->settings['type'], SOCK_STREAM, $this->settings['protocol']);
|
||||
$this->sock->bind($this->settings['address'], $this->settings['port']);
|
||||
$this->sock->listen();
|
||||
$this->sock->setBlocking(true);
|
||||
|
||||
$timeout = 2;
|
||||
$this->sock->setOption(\SOL_SOCKET, \SO_RCVTIMEO, $timeout);
|
||||
$this->sock->setOption(\SOL_SOCKET, \SO_SNDTIMEO, $timeout);
|
||||
\danog\MadelineProto\Logger::log('Server started! Listening on ' . $this->settings['address'] . ':' . $this->settings['port']);
|
||||
while (true) {
|
||||
\pcntl_signal_dispatch();
|
||||
|
||||
try {
|
||||
if ($sock = $this->sock->accept()) {
|
||||
$this->handle($sock);
|
||||
@ -70,7 +63,6 @@ class Server
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function handle($socket)
|
||||
{
|
||||
$pid = \pcntl_fork();
|
||||
@ -83,29 +75,25 @@ class Server
|
||||
$handler->loop();
|
||||
die;
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
if ($this->mypid === \getmypid()) {
|
||||
\danog\MadelineProto\Logger::log('Shutting main process ' . $this->mypid . ' down');
|
||||
unset($this->sock);
|
||||
foreach ($this->pids as $pid) {
|
||||
\danog\MadelineProto\Logger::log("Waiting for $pid");
|
||||
\danog\MadelineProto\Logger::log("Waiting for {$pid}");
|
||||
\pcntl_wait($pid);
|
||||
}
|
||||
\danog\MadelineProto\Logger::log('Done, closing main process');
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
public function sigHandler($sig)
|
||||
{
|
||||
switch ($sig) {
|
||||
case SIGTERM:
|
||||
case SIGINT:
|
||||
exit();
|
||||
|
||||
exit;
|
||||
case SIGCHLD:
|
||||
\pcntl_waitpid(-1, $status);
|
||||
break;
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Shutdown module.
|
||||
*
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* ADNL stream wrapper.
|
||||
*
|
||||
@ -24,7 +25,6 @@ use danog\MadelineProto\Stream\Async\BufferedStream;
|
||||
use danog\MadelineProto\Stream\BufferedStreamInterface;
|
||||
use danog\MadelineProto\Stream\ConnectionContext;
|
||||
use danog\MadelineProto\Stream\MTProtoBufferInterface;
|
||||
|
||||
use danog\MadelineProto\Stream\RawStreamInterface;
|
||||
use danog\MadelineProto\Tools;
|
||||
|
||||
@ -39,7 +39,6 @@ class ADNLStream implements BufferedStreamInterface, MTProtoBufferInterface
|
||||
{
|
||||
use BufferedStream;
|
||||
private $stream;
|
||||
|
||||
/**
|
||||
* Connect to stream.
|
||||
*
|
||||
@ -51,7 +50,6 @@ class ADNLStream implements BufferedStreamInterface, MTProtoBufferInterface
|
||||
{
|
||||
$this->stream = yield $ctx->getStream($header);
|
||||
}
|
||||
|
||||
/**
|
||||
* Async close.
|
||||
*
|
||||
@ -61,7 +59,6 @@ class ADNLStream implements BufferedStreamInterface, MTProtoBufferInterface
|
||||
{
|
||||
return $this->stream->disconnect();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get write buffer asynchronously.
|
||||
*
|
||||
@ -77,10 +74,8 @@ class ADNLStream implements BufferedStreamInterface, MTProtoBufferInterface
|
||||
$this->stream->startWriteHash();
|
||||
$this->stream->checkWriteHash($length - 32);
|
||||
yield $buffer->bufferWrite(Tools::random(32));
|
||||
|
||||
return $buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get read buffer asynchronously.
|
||||
*
|
||||
@ -96,10 +91,8 @@ class ADNLStream implements BufferedStreamInterface, MTProtoBufferInterface
|
||||
$this->stream->checkReadHash($length);
|
||||
yield $buffer->bufferRead(32);
|
||||
$length -= 32;
|
||||
|
||||
return $buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
@ -118,8 +111,6 @@ class ADNLStream implements BufferedStreamInterface, MTProtoBufferInterface
|
||||
{
|
||||
return $this->stream;
|
||||
}
|
||||
|
||||
|
||||
public static function getName(): string
|
||||
{
|
||||
return __CLASS__;
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Buffer helper trait.
|
||||
*
|
||||
@ -33,7 +34,6 @@ trait Buffer
|
||||
{
|
||||
return \danog\MadelineProto\Tools::call($this->bufferReadGenerator($length));
|
||||
}
|
||||
|
||||
public function bufferWrite(string $data): Promise
|
||||
{
|
||||
return \danog\MadelineProto\Tools::call($this->bufferWriteGenerator($data));
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Buffered stream helper trait.
|
||||
*
|
||||
@ -30,7 +31,6 @@ use Amp\Promise;
|
||||
trait BufferedStream
|
||||
{
|
||||
use Stream;
|
||||
|
||||
/**
|
||||
* Get read buffer asynchronously.
|
||||
*
|
||||
@ -42,7 +42,6 @@ trait BufferedStream
|
||||
{
|
||||
return \danog\MadelineProto\Tools::call($this->getReadBufferGenerator($length));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get write buffer asynchronously.
|
||||
*
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Raw stream helper trait.
|
||||
*
|
||||
@ -30,17 +31,14 @@ use Amp\Promise;
|
||||
trait RawStream
|
||||
{
|
||||
use Stream;
|
||||
|
||||
public function read(): Promise
|
||||
{
|
||||
return \danog\MadelineProto\Tools::call($this->readGenerator());
|
||||
}
|
||||
|
||||
public function write(string $data): Promise
|
||||
{
|
||||
return \danog\MadelineProto\Tools::call($this->writeGenerator($data));
|
||||
}
|
||||
|
||||
public function end(string $finalData = ''): Promise
|
||||
{
|
||||
return \danog\MadelineProto\Tools::call($this->endGenerator($finalData));
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Generic stream helper trait.
|
||||
*
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Buffer interface.
|
||||
*
|
||||
@ -35,7 +36,6 @@ interface BufferInterface
|
||||
* @return Promise
|
||||
*/
|
||||
public function bufferRead(int $length): Promise;
|
||||
|
||||
/**
|
||||
* Write data asynchronously.
|
||||
*
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Buffered proxy stream interface.
|
||||
*
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Buffered stream interface.
|
||||
*
|
||||
@ -35,7 +36,6 @@ interface BufferedStreamInterface extends StreamInterface
|
||||
* @return Promise
|
||||
*/
|
||||
public function getReadBuffer(&$length): Promise;
|
||||
|
||||
/**
|
||||
* Get write buffer asynchronously.
|
||||
*
|
||||
@ -44,7 +44,6 @@ interface BufferedStreamInterface extends StreamInterface
|
||||
* @return Promise
|
||||
*/
|
||||
public function getWriteBuffer(int $length, string $append = ''): Promise;
|
||||
|
||||
/**
|
||||
* Get stream name.
|
||||
*
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Buffered raw stream.
|
||||
*
|
||||
@ -36,14 +37,11 @@ use danog\MadelineProto\Stream\RawStreamInterface;
|
||||
class BufferedRawStream implements BufferedStreamInterface, BufferInterface, RawStreamInterface
|
||||
{
|
||||
use RawStream;
|
||||
|
||||
const MAX_SIZE = 10 * 1024 * 1024;
|
||||
|
||||
protected $stream;
|
||||
protected $memory_stream;
|
||||
private $append = '';
|
||||
private $append_after = 0;
|
||||
|
||||
/**
|
||||
* Asynchronously connect to a TCP/TLS server.
|
||||
*
|
||||
@ -55,10 +53,8 @@ class BufferedRawStream implements BufferedStreamInterface, BufferInterface, Raw
|
||||
{
|
||||
$this->stream = yield $ctx->getStream($header);
|
||||
$this->memory_stream = \fopen('php://memory', 'r+');
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Async chunked read.
|
||||
*
|
||||
@ -71,7 +67,6 @@ class BufferedRawStream implements BufferedStreamInterface, BufferInterface, Raw
|
||||
}
|
||||
return $this->stream->read();
|
||||
}
|
||||
|
||||
/**
|
||||
* Async write.
|
||||
*
|
||||
@ -86,7 +81,6 @@ class BufferedRawStream implements BufferedStreamInterface, BufferInterface, Raw
|
||||
}
|
||||
return $this->stream->write($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Async close.
|
||||
*
|
||||
@ -103,7 +97,6 @@ class BufferedRawStream implements BufferedStreamInterface, BufferInterface, Raw
|
||||
$this->stream = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get read buffer asynchronously.
|
||||
*
|
||||
@ -128,10 +121,8 @@ class BufferedRawStream implements BufferedStreamInterface, BufferInterface, Raw
|
||||
\fclose($this->memory_stream);
|
||||
$this->memory_stream = $new_memory_stream;
|
||||
}
|
||||
|
||||
return new \Amp\Success($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get write buffer asynchronously.
|
||||
*
|
||||
@ -145,10 +136,8 @@ class BufferedRawStream implements BufferedStreamInterface, BufferInterface, Raw
|
||||
$this->append = $append;
|
||||
$this->append_after = $length - \strlen($append);
|
||||
}
|
||||
|
||||
return new \Amp\Success($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read data asynchronously.
|
||||
*
|
||||
@ -169,7 +158,6 @@ class BufferedRawStream implements BufferedStreamInterface, BufferInterface, Raw
|
||||
}
|
||||
return \danog\MadelineProto\Tools::call($this->bufferReadGenerator($length));
|
||||
}
|
||||
|
||||
/**
|
||||
* Read data asynchronously.
|
||||
*
|
||||
@ -185,22 +173,18 @@ class BufferedRawStream implements BufferedStreamInterface, BufferInterface, Raw
|
||||
if ($buffer_length < $length && $buffer_length) {
|
||||
\fseek($this->memory_stream, $offset + $buffer_length);
|
||||
}
|
||||
|
||||
while ($buffer_length < $length) {
|
||||
$chunk = yield $this->read();
|
||||
if ($chunk === null) {
|
||||
$this->disconnect();
|
||||
|
||||
throw new \danog\MadelineProto\NothingInTheSocketException();
|
||||
}
|
||||
\fwrite($this->memory_stream, $chunk);
|
||||
$buffer_length += \strlen($chunk);
|
||||
}
|
||||
\fseek($this->memory_stream, $offset);
|
||||
|
||||
return \fread($this->memory_stream, $length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Async write.
|
||||
*
|
||||
@ -218,14 +202,11 @@ class BufferedRawStream implements BufferedStreamInterface, BufferInterface, Raw
|
||||
} elseif ($this->append_after < 0) {
|
||||
$this->append_after = 0;
|
||||
$this->append = '';
|
||||
|
||||
throw new Exception('Tried to send too much out of frame data, cannot append');
|
||||
}
|
||||
}
|
||||
|
||||
return $this->write($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
@ -244,7 +225,6 @@ class BufferedRawStream implements BufferedStreamInterface, BufferInterface, Raw
|
||||
{
|
||||
return $this->stream;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get class name.
|
||||
*
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* AES CTR stream wrapper.
|
||||
*
|
||||
@ -26,7 +27,6 @@ use danog\MadelineProto\Stream\BufferedProxyStreamInterface;
|
||||
use danog\MadelineProto\Stream\BufferInterface;
|
||||
use danog\MadelineProto\Stream\ConnectionContext;
|
||||
use danog\MadelineProto\Stream\RawStreamInterface;
|
||||
|
||||
use tgseclib\Crypt\AES;
|
||||
|
||||
/**
|
||||
@ -48,7 +48,6 @@ class CtrStream implements BufferedProxyStreamInterface, BufferInterface
|
||||
private $extra;
|
||||
private $append = '';
|
||||
private $append_after = 0;
|
||||
|
||||
/**
|
||||
* Connect to stream.
|
||||
*
|
||||
@ -62,15 +61,12 @@ class CtrStream implements BufferedProxyStreamInterface, BufferInterface
|
||||
$this->encrypt->enableContinuousBuffer();
|
||||
$this->encrypt->setKey($this->extra['encrypt']['key']);
|
||||
$this->encrypt->setIV($this->extra['encrypt']['iv']);
|
||||
|
||||
$this->decrypt = new \tgseclib\Crypt\AES('ctr');
|
||||
$this->decrypt->enableContinuousBuffer();
|
||||
$this->decrypt->setKey($this->extra['decrypt']['key']);
|
||||
$this->decrypt->setIV($this->extra['decrypt']['iv']);
|
||||
|
||||
$this->stream = yield $ctx->getStream($header);
|
||||
}
|
||||
|
||||
/**
|
||||
* Async close.
|
||||
*
|
||||
@ -80,7 +76,6 @@ class CtrStream implements BufferedProxyStreamInterface, BufferInterface
|
||||
{
|
||||
return $this->stream->disconnect();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get write buffer asynchronously.
|
||||
*
|
||||
@ -95,10 +90,8 @@ class CtrStream implements BufferedProxyStreamInterface, BufferInterface
|
||||
$this->append = $append;
|
||||
$this->append_after = $length - \strlen($append);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get read buffer asynchronously.
|
||||
*
|
||||
@ -109,10 +102,8 @@ class CtrStream implements BufferedProxyStreamInterface, BufferInterface
|
||||
public function getReadBufferGenerator(&$length): \Generator
|
||||
{
|
||||
$this->read_buffer = yield $this->stream->getReadBuffer($length);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypts read data asynchronously.
|
||||
*
|
||||
@ -126,7 +117,6 @@ class CtrStream implements BufferedProxyStreamInterface, BufferInterface
|
||||
{
|
||||
return @$this->decrypt->encrypt(yield $this->read_buffer->bufferRead($length));
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes data to the stream.
|
||||
*
|
||||
@ -146,14 +136,11 @@ class CtrStream implements BufferedProxyStreamInterface, BufferInterface
|
||||
} elseif ($this->append_after < 0) {
|
||||
$this->append_after = 0;
|
||||
$this->append = '';
|
||||
|
||||
throw new \danog\MadelineProto\Exception('Tried to send too much out of frame data, cannot append');
|
||||
}
|
||||
}
|
||||
|
||||
return $this->write_buffer->bufferWrite(@$this->encrypt->encrypt($data));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set obfuscation keys/IVs.
|
||||
*
|
||||
@ -165,7 +152,6 @@ class CtrStream implements BufferedProxyStreamInterface, BufferInterface
|
||||
{
|
||||
$this->extra = $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
@ -175,7 +161,6 @@ class CtrStream implements BufferedProxyStreamInterface, BufferInterface
|
||||
{
|
||||
return $this->stream->getSocket();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
@ -193,7 +178,6 @@ class CtrStream implements BufferedProxyStreamInterface, BufferInterface
|
||||
{
|
||||
return $this->decrypt;
|
||||
}
|
||||
|
||||
public static function getName(): string
|
||||
{
|
||||
return __CLASS__;
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Hash stream wrapper.
|
||||
*
|
||||
@ -23,7 +24,6 @@ use danog\MadelineProto\Stream\Async\BufferedStream;
|
||||
use danog\MadelineProto\Stream\BufferedProxyStreamInterface;
|
||||
use danog\MadelineProto\Stream\BufferInterface;
|
||||
use danog\MadelineProto\Stream\ConnectionContext;
|
||||
|
||||
use danog\MadelineProto\Stream\RawStreamInterface;
|
||||
|
||||
/**
|
||||
@ -45,7 +45,6 @@ class HashedBufferedStream implements BufferedProxyStreamInterface, BufferInterf
|
||||
private $read_check_pos = 0;
|
||||
private $stream;
|
||||
private $rev = false;
|
||||
|
||||
/**
|
||||
* Enable read hashing.
|
||||
*
|
||||
@ -55,7 +54,6 @@ class HashedBufferedStream implements BufferedProxyStreamInterface, BufferInterf
|
||||
{
|
||||
$this->read_hash = \hash_init($this->hash_name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the read hash after N bytes are read.
|
||||
*
|
||||
@ -67,7 +65,6 @@ class HashedBufferedStream implements BufferedProxyStreamInterface, BufferInterf
|
||||
{
|
||||
$this->read_check_after = $after;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop read hashing and get final hash.
|
||||
*
|
||||
@ -82,10 +79,8 @@ class HashedBufferedStream implements BufferedProxyStreamInterface, BufferInterf
|
||||
$this->read_hash = null;
|
||||
$this->read_check_after = 0;
|
||||
$this->read_check_pos = 0;
|
||||
|
||||
return $hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if we are read hashing.
|
||||
*
|
||||
@ -95,7 +90,6 @@ class HashedBufferedStream implements BufferedProxyStreamInterface, BufferInterf
|
||||
{
|
||||
return $this->read_hash !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable write hashing.
|
||||
*
|
||||
@ -105,7 +99,6 @@ class HashedBufferedStream implements BufferedProxyStreamInterface, BufferInterf
|
||||
{
|
||||
$this->write_hash = \hash_init($this->hash_name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the write hash after N bytes are read.
|
||||
*
|
||||
@ -117,7 +110,6 @@ class HashedBufferedStream implements BufferedProxyStreamInterface, BufferInterf
|
||||
{
|
||||
$this->write_check_after = $after;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop write hashing and get final hash.
|
||||
*
|
||||
@ -132,10 +124,8 @@ class HashedBufferedStream implements BufferedProxyStreamInterface, BufferInterf
|
||||
$this->write_hash = null;
|
||||
$this->write_check_after = 0;
|
||||
$this->write_check_pos = 0;
|
||||
|
||||
return $hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if we are write hashing.
|
||||
*
|
||||
@ -145,7 +135,6 @@ class HashedBufferedStream implements BufferedProxyStreamInterface, BufferInterf
|
||||
{
|
||||
return $this->write_hash !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hashes read data asynchronously.
|
||||
*
|
||||
@ -167,7 +156,6 @@ class HashedBufferedStream implements BufferedProxyStreamInterface, BufferInterf
|
||||
if ($hash !== yield $this->read_buffer->bufferRead(\strlen($hash))) {
|
||||
throw new \danog\MadelineProto\Exception('Hash mismatch');
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
$data = yield $this->read_buffer->bufferRead($length);
|
||||
@ -175,10 +163,8 @@ class HashedBufferedStream implements BufferedProxyStreamInterface, BufferInterf
|
||||
if ($this->read_check_after) {
|
||||
$this->read_check_pos += $length;
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the hash algorithm.
|
||||
*
|
||||
@ -196,7 +182,6 @@ class HashedBufferedStream implements BufferedProxyStreamInterface, BufferInterf
|
||||
}
|
||||
$this->hash_name = $hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect to stream.
|
||||
*
|
||||
@ -212,10 +197,8 @@ class HashedBufferedStream implements BufferedProxyStreamInterface, BufferInterf
|
||||
$this->read_hash = null;
|
||||
$this->read_check_after = 0;
|
||||
$this->read_check_pos = 0;
|
||||
|
||||
$this->stream = yield $ctx->getStream($header);
|
||||
}
|
||||
|
||||
/**
|
||||
* Async close.
|
||||
*
|
||||
@ -225,7 +208,6 @@ class HashedBufferedStream implements BufferedProxyStreamInterface, BufferInterf
|
||||
{
|
||||
return $this->stream->disconnect();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get read buffer asynchronously.
|
||||
*
|
||||
@ -237,13 +219,10 @@ class HashedBufferedStream implements BufferedProxyStreamInterface, BufferInterf
|
||||
{
|
||||
//if ($this->read_hash) {
|
||||
$this->read_buffer = yield $this->stream->getReadBuffer($length);
|
||||
|
||||
return $this;
|
||||
//}
|
||||
|
||||
//return yield $this->stream->getReadBuffer($length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get write buffer asynchronously.
|
||||
*
|
||||
@ -255,13 +234,10 @@ class HashedBufferedStream implements BufferedProxyStreamInterface, BufferInterf
|
||||
{
|
||||
//if ($this->write_hash) {
|
||||
$this->write_buffer = yield $this->stream->getWriteBuffer($length, $append);
|
||||
|
||||
return $this;
|
||||
//}
|
||||
|
||||
//return yield $this->stream->getWriteBuffer($length, $append);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads data from the stream.
|
||||
*
|
||||
@ -274,10 +250,8 @@ class HashedBufferedStream implements BufferedProxyStreamInterface, BufferInterf
|
||||
if ($this->read_hash === null) {
|
||||
return $this->read_buffer->bufferRead($length);
|
||||
}
|
||||
|
||||
return \danog\MadelineProto\Tools::call($this->bufferReadGenerator($length));
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes data to the stream.
|
||||
*
|
||||
@ -292,14 +266,12 @@ class HashedBufferedStream implements BufferedProxyStreamInterface, BufferInterf
|
||||
if ($this->write_hash === null) {
|
||||
return $this->write_buffer->bufferWrite($data);
|
||||
}
|
||||
|
||||
$length = \strlen($data);
|
||||
if ($this->write_check_after && $length + $this->write_check_pos >= $this->write_check_after) {
|
||||
if ($length + $this->write_check_pos > $this->write_check_after) {
|
||||
throw new \danog\MadelineProto\Exception('Too much out of frame data was sent, cannot check hash');
|
||||
}
|
||||
\hash_update($this->write_hash, $data);
|
||||
|
||||
return $this->write_buffer->bufferWrite($data . $this->getWriteHash());
|
||||
}
|
||||
if ($this->write_check_after) {
|
||||
@ -308,10 +280,8 @@ class HashedBufferedStream implements BufferedProxyStreamInterface, BufferInterf
|
||||
if ($this->write_hash) {
|
||||
\hash_update($this->write_hash, $data);
|
||||
}
|
||||
|
||||
return $this->write_buffer->bufferWrite($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
@ -321,7 +291,6 @@ class HashedBufferedStream implements BufferedProxyStreamInterface, BufferInterf
|
||||
{
|
||||
return $this->stream->getSocket();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Buffered raw stream.
|
||||
*
|
||||
@ -44,7 +45,6 @@ class SimpleBufferedRawStream extends BufferedRawStream implements BufferedStrea
|
||||
if ($buffer_length < $length && $buffer_length) {
|
||||
\fseek($this->memory_stream, $offset + $buffer_length);
|
||||
}
|
||||
|
||||
while ($buffer_length < $length) {
|
||||
$chunk = yield $this->read();
|
||||
if ($chunk === null) {
|
||||
@ -55,7 +55,6 @@ class SimpleBufferedRawStream extends BufferedRawStream implements BufferedStrea
|
||||
$buffer_length += \strlen($chunk);
|
||||
}
|
||||
\fseek($this->memory_stream, $offset);
|
||||
|
||||
return \fread($this->memory_stream, $length);
|
||||
}
|
||||
/**
|
||||
@ -67,7 +66,6 @@ class SimpleBufferedRawStream extends BufferedRawStream implements BufferedStrea
|
||||
{
|
||||
return $this->stream;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get class name.
|
||||
*
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Connection context.
|
||||
*
|
||||
@ -107,14 +108,12 @@ class ConnectionContext
|
||||
* @var int
|
||||
*/
|
||||
private $key = 0;
|
||||
|
||||
/**
|
||||
* Read callback.
|
||||
*
|
||||
* @var callable
|
||||
*/
|
||||
private $readCallback;
|
||||
|
||||
/**
|
||||
* Set the socket context.
|
||||
*
|
||||
@ -125,10 +124,8 @@ class ConnectionContext
|
||||
public function setSocketContext(ConnectContext $socketContext): self
|
||||
{
|
||||
$this->socketContext = $socketContext;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the socket context.
|
||||
*
|
||||
@ -138,7 +135,6 @@ class ConnectionContext
|
||||
{
|
||||
return $this->socketContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the connection URI.
|
||||
*
|
||||
@ -149,10 +145,8 @@ class ConnectionContext
|
||||
public function setUri($uri): self
|
||||
{
|
||||
$this->uri = $uri instanceof Uri ? $uri : new Uri($uri);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the URI as a string.
|
||||
*
|
||||
@ -162,7 +156,6 @@ class ConnectionContext
|
||||
{
|
||||
return (string) $this->uri;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the URI.
|
||||
*
|
||||
@ -172,7 +165,6 @@ class ConnectionContext
|
||||
{
|
||||
return $this->uri;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the cancellation token.
|
||||
*
|
||||
@ -183,10 +175,8 @@ class ConnectionContext
|
||||
public function setCancellationToken($cancellationToken): self
|
||||
{
|
||||
$this->cancellationToken = $cancellationToken;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the cancellation token.
|
||||
*
|
||||
@ -215,10 +205,8 @@ class ConnectionContext
|
||||
public function setTest(bool $test): self
|
||||
{
|
||||
$this->test = $test;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this is a test connection.
|
||||
*
|
||||
@ -237,7 +225,6 @@ class ConnectionContext
|
||||
{
|
||||
return $this->media;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this is a CDN connection.
|
||||
*
|
||||
@ -247,7 +234,6 @@ class ConnectionContext
|
||||
{
|
||||
return $this->cdn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this connection context will only be used by the DNS client.
|
||||
*
|
||||
@ -257,7 +243,6 @@ class ConnectionContext
|
||||
{
|
||||
return $this->isDns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this connection context will only be used by the DNS client.
|
||||
*
|
||||
@ -279,10 +264,8 @@ class ConnectionContext
|
||||
public function secure(bool $secure): self
|
||||
{
|
||||
$this->secure = $secure;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether to use TLS with socket connections.
|
||||
*
|
||||
@ -292,7 +275,6 @@ class ConnectionContext
|
||||
{
|
||||
return $this->secure;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the DC ID.
|
||||
*
|
||||
@ -304,15 +286,13 @@ class ConnectionContext
|
||||
{
|
||||
$int = \intval($dc);
|
||||
if (!(1 <= $int && $int <= 1000)) {
|
||||
throw new Exception("Invalid DC id provided: $dc");
|
||||
throw new Exception("Invalid DC id provided: {$dc}");
|
||||
}
|
||||
$this->dc = $dc;
|
||||
$this->media = \strpos($dc, '_media') !== false;
|
||||
$this->cdn = \strpos($dc, '_cdn') !== false;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the DC ID.
|
||||
*
|
||||
@ -322,7 +302,6 @@ class ConnectionContext
|
||||
{
|
||||
return $this->dc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the int DC ID.
|
||||
*
|
||||
@ -337,10 +316,8 @@ class ConnectionContext
|
||||
if ($this->media) {
|
||||
$dc = -$dc;
|
||||
}
|
||||
|
||||
return $dc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether to use ipv6.
|
||||
*
|
||||
@ -351,10 +328,8 @@ class ConnectionContext
|
||||
public function setIpv6(bool $ipv6): self
|
||||
{
|
||||
$this->ipv6 = $ipv6;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether to use ipv6.
|
||||
*
|
||||
@ -364,7 +339,6 @@ class ConnectionContext
|
||||
{
|
||||
return $this->ipv6;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a stream to the stream chain.
|
||||
*
|
||||
@ -377,10 +351,8 @@ class ConnectionContext
|
||||
{
|
||||
$this->nextStreams[] = [$streamName, $extra];
|
||||
$this->key = \count($this->nextStreams) - 1;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set read callback, called every time the socket reads at least a byte.
|
||||
*
|
||||
@ -392,7 +364,6 @@ class ConnectionContext
|
||||
{
|
||||
$this->readCallback = $callable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a read callback is present.
|
||||
*
|
||||
@ -402,7 +373,6 @@ class ConnectionContext
|
||||
{
|
||||
return $this->readCallback !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get read callback.
|
||||
*
|
||||
@ -412,7 +382,6 @@ class ConnectionContext
|
||||
{
|
||||
return $this->readCallback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current stream name from the stream chain.
|
||||
*
|
||||
@ -422,7 +391,6 @@ class ConnectionContext
|
||||
{
|
||||
return $this->nextStreams[$this->key][0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if has stream within stream chain.
|
||||
*
|
||||
@ -439,7 +407,6 @@ class ConnectionContext
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a stream from the stream chain.
|
||||
*
|
||||
@ -455,11 +422,8 @@ class ConnectionContext
|
||||
$obj->setExtra($extra);
|
||||
}
|
||||
yield $obj->connect($this, $buffer);
|
||||
|
||||
return $obj;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the inputClientProxy proxy MTProto object.
|
||||
*
|
||||
@ -502,10 +466,8 @@ class ConnectionContext
|
||||
$string .= ' (' . \json_encode($stream[1]) . ')';
|
||||
}
|
||||
}
|
||||
|
||||
return $string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a representation of the context.
|
||||
*
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* MTProto buffer interface.
|
||||
*
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Abridged stream wrapper.
|
||||
*
|
||||
@ -34,9 +35,7 @@ use danog\MadelineProto\Stream\RawStreamInterface;
|
||||
class AbridgedStream implements BufferedStreamInterface, MTProtoBufferInterface
|
||||
{
|
||||
use BufferedStream;
|
||||
|
||||
private $stream;
|
||||
|
||||
/**
|
||||
* Connect to stream.
|
||||
*
|
||||
@ -48,7 +47,6 @@ class AbridgedStream implements BufferedStreamInterface, MTProtoBufferInterface
|
||||
{
|
||||
$this->stream = yield $ctx->getStream(\chr(239) . $header);
|
||||
}
|
||||
|
||||
/**
|
||||
* Async close.
|
||||
*
|
||||
@ -58,7 +56,6 @@ class AbridgedStream implements BufferedStreamInterface, MTProtoBufferInterface
|
||||
{
|
||||
return $this->stream->disconnect();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get write buffer asynchronously.
|
||||
*
|
||||
@ -76,10 +73,8 @@ class AbridgedStream implements BufferedStreamInterface, MTProtoBufferInterface
|
||||
}
|
||||
$buffer = yield $this->stream->getWriteBuffer(\strlen($message) + $length, $append);
|
||||
yield $buffer->bufferWrite($message);
|
||||
|
||||
return $buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get read buffer asynchronously.
|
||||
*
|
||||
@ -92,13 +87,11 @@ class AbridgedStream implements BufferedStreamInterface, MTProtoBufferInterface
|
||||
$buffer = yield $this->stream->getReadBuffer($l);
|
||||
$length = \ord(yield $buffer->bufferRead(1));
|
||||
if ($length >= 127) {
|
||||
$length = \unpack('V', (yield $buffer->bufferRead(3))."\0")[1];
|
||||
$length = \unpack('V', yield $buffer->bufferRead(3) . "\0")[1];
|
||||
}
|
||||
$length <<= 2;
|
||||
|
||||
return $buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
@ -108,7 +101,6 @@ class AbridgedStream implements BufferedStreamInterface, MTProtoBufferInterface
|
||||
{
|
||||
return $this->stream->getSocket();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* TCP full stream wrapper.
|
||||
*
|
||||
@ -40,7 +41,6 @@ class FullStream implements BufferedStreamInterface, MTProtoBufferInterface
|
||||
private $stream;
|
||||
private $in_seq_no = -1;
|
||||
private $out_seq_no = -1;
|
||||
|
||||
/**
|
||||
* Stream to use as data source.
|
||||
*
|
||||
@ -54,10 +54,8 @@ class FullStream implements BufferedStreamInterface, MTProtoBufferInterface
|
||||
$this->out_seq_no = -1;
|
||||
$this->stream = new HashedBufferedStream();
|
||||
$this->stream->setExtra('crc32b_rev');
|
||||
|
||||
return $this->stream->connect($ctx, $header);
|
||||
}
|
||||
|
||||
/**
|
||||
* Async close.
|
||||
*
|
||||
@ -67,7 +65,6 @@ class FullStream implements BufferedStreamInterface, MTProtoBufferInterface
|
||||
{
|
||||
return $this->stream->disconnect();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get write buffer asynchronously.
|
||||
*
|
||||
@ -82,10 +79,8 @@ class FullStream implements BufferedStreamInterface, MTProtoBufferInterface
|
||||
$buffer = yield $this->stream->getWriteBuffer($length + 12, $append);
|
||||
$this->out_seq_no++;
|
||||
$buffer->bufferWrite(\pack('VV', $length + 12, $this->out_seq_no));
|
||||
|
||||
return $buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get read buffer asynchronously.
|
||||
*
|
||||
@ -105,10 +100,8 @@ class FullStream implements BufferedStreamInterface, MTProtoBufferInterface
|
||||
if ($in_seq_no != $this->in_seq_no) {
|
||||
throw new \danog\MadelineProto\Exception('Incoming seq_no mismatch');
|
||||
}
|
||||
|
||||
return $buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
@ -118,7 +111,6 @@ class FullStream implements BufferedStreamInterface, MTProtoBufferInterface
|
||||
{
|
||||
return $this->stream->getSocket();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* HTTP stream wrapper.
|
||||
*
|
||||
@ -45,7 +46,6 @@ class HttpStream implements MTProtoBufferInterface, BufferedProxyStreamInterface
|
||||
* @var \Amp\Uri\Uri
|
||||
*/
|
||||
private $uri;
|
||||
|
||||
/**
|
||||
* Connect to stream.
|
||||
*
|
||||
@ -59,7 +59,6 @@ class HttpStream implements MTProtoBufferInterface, BufferedProxyStreamInterface
|
||||
$this->stream = yield $ctx->getStream($header);
|
||||
$this->uri = $ctx->getUri();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set proxy data.
|
||||
*
|
||||
@ -73,7 +72,6 @@ class HttpStream implements MTProtoBufferInterface, BufferedProxyStreamInterface
|
||||
$this->header = \base64_encode($extra['user'] . ':' . $extra['password']) . "\r\n";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Async close.
|
||||
*
|
||||
@ -83,7 +81,6 @@ class HttpStream implements MTProtoBufferInterface, BufferedProxyStreamInterface
|
||||
{
|
||||
return $this->stream->disconnect();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get write buffer asynchronously.
|
||||
*
|
||||
@ -96,10 +93,8 @@ class HttpStream implements MTProtoBufferInterface, BufferedProxyStreamInterface
|
||||
$headers = 'POST ' . $this->uri->getPath() . " HTTP/1.1\r\nHost: " . $this->uri->getHost() . ':' . $this->uri->getPort() . "\r\n" . "Content-Type: application/x-www-form-urlencoded\r\nConnection: keep-alive\r\nKeep-Alive: timeout=100000, max=10000000\r\nContent-Length: " . $length . $this->header . "\r\n\r\n";
|
||||
$buffer = yield $this->stream->getWriteBuffer(\strlen($headers) + $length, $append);
|
||||
yield $buffer->bufferWrite($headers);
|
||||
|
||||
return $buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get read buffer asynchronously.
|
||||
*
|
||||
@ -115,7 +110,8 @@ class HttpStream implements MTProtoBufferInterface, BufferedProxyStreamInterface
|
||||
while (true) {
|
||||
$piece = yield $buffer->bufferRead(2);
|
||||
$headers .= $piece;
|
||||
if ($piece === "\n\r") { // Assume end of headers with \r\n\r\n
|
||||
if ($piece === "\n\r") {
|
||||
// Assume end of headers with \r\n\r\n
|
||||
$headers .= yield $buffer->bufferRead(1);
|
||||
break;
|
||||
}
|
||||
@ -125,7 +121,6 @@ class HttpStream implements MTProtoBufferInterface, BufferedProxyStreamInterface
|
||||
$was_crlf = $piece === "\r\n";
|
||||
}
|
||||
$headers = \explode("\r\n", $headers);
|
||||
|
||||
list($protocol, $code, $description) = \explode(' ', $headers[0], 3);
|
||||
list($protocol, $protocol_version) = \explode('/', $protocol);
|
||||
if ($protocol !== 'HTTP') {
|
||||
@ -141,31 +136,24 @@ class HttpStream implements MTProtoBufferInterface, BufferedProxyStreamInterface
|
||||
$current_header = \explode(':', $current_header, 2);
|
||||
$headers[\strtolower($current_header[0])] = \trim($current_header[1]);
|
||||
}
|
||||
|
||||
$close = $protocol === 'HTTP/1.0';
|
||||
if (isset($headers['connection'])) {
|
||||
$close = \strtolower($headers['connection']) === 'close';
|
||||
}
|
||||
|
||||
if ($code !== 200) {
|
||||
$read = '';
|
||||
if (isset($headers['content-length'])) {
|
||||
$read = yield $buffer->bufferRead((int) $headers['content-length']);
|
||||
}
|
||||
|
||||
if ($close) {
|
||||
$this->disconnect();
|
||||
yield $this->connect($this->ctx);
|
||||
}
|
||||
|
||||
\danog\MadelineProto\Logger::log($read);
|
||||
|
||||
$this->code = \danog\MadelineProto\Tools::packSignedInt(-$code);
|
||||
$length = 4;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
if ($close) {
|
||||
$this->stream->disconnect();
|
||||
yield $this->stream->connect($this->ctx);
|
||||
@ -173,15 +161,12 @@ class HttpStream implements MTProtoBufferInterface, BufferedProxyStreamInterface
|
||||
if (isset($headers['content-length'])) {
|
||||
$length = (int) $headers['content-length'];
|
||||
}
|
||||
|
||||
return $buffer;
|
||||
}
|
||||
|
||||
public function bufferRead(int $length): Promise
|
||||
{
|
||||
return new Success($this->code);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
@ -200,7 +185,6 @@ class HttpStream implements MTProtoBufferInterface, BufferedProxyStreamInterface
|
||||
{
|
||||
return $this->stream;
|
||||
}
|
||||
|
||||
public static function getName(): string
|
||||
{
|
||||
return __CLASS__;
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* HTTPS stream wrapper.
|
||||
*
|
||||
@ -20,7 +21,6 @@ namespace danog\MadelineProto\Stream\MTProtoTransport;
|
||||
|
||||
use danog\MadelineProto\Stream\ConnectionContext;
|
||||
use danog\MadelineProto\Stream\MTProtoBufferInterface;
|
||||
|
||||
use danog\MadelineProto\Stream\RawStreamInterface;
|
||||
|
||||
/**
|
||||
@ -41,7 +41,6 @@ class HttpsStream extends HttpStream implements MTProtoBufferInterface
|
||||
{
|
||||
return parent::connectGenerator($ctx->getCtx()->secure(true), $header);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* TCP Intermediate stream wrapper.
|
||||
*
|
||||
@ -24,7 +25,6 @@ use danog\MadelineProto\Stream\Async\BufferedStream;
|
||||
use danog\MadelineProto\Stream\BufferedStreamInterface;
|
||||
use danog\MadelineProto\Stream\ConnectionContext;
|
||||
use danog\MadelineProto\Stream\MTProtoBufferInterface;
|
||||
|
||||
use danog\MadelineProto\Stream\RawStreamInterface;
|
||||
|
||||
/**
|
||||
@ -38,7 +38,6 @@ class IntermediatePaddedStream implements BufferedStreamInterface, MTProtoBuffer
|
||||
{
|
||||
use BufferedStream;
|
||||
private $stream;
|
||||
|
||||
/**
|
||||
* Connect to stream.
|
||||
*
|
||||
@ -50,7 +49,6 @@ class IntermediatePaddedStream implements BufferedStreamInterface, MTProtoBuffer
|
||||
{
|
||||
$this->stream = yield $ctx->getStream(\str_repeat(\chr(221), 4) . $header);
|
||||
}
|
||||
|
||||
/**
|
||||
* Async close.
|
||||
*
|
||||
@ -60,7 +58,6 @@ class IntermediatePaddedStream implements BufferedStreamInterface, MTProtoBuffer
|
||||
{
|
||||
return $this->stream->disconnect();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get write buffer asynchronously.
|
||||
*
|
||||
@ -73,10 +70,8 @@ class IntermediatePaddedStream implements BufferedStreamInterface, MTProtoBuffer
|
||||
$padding_length = \danog\MadelineProto\Tools::randomInt($modulus = 16);
|
||||
$buffer = yield $this->stream->getWriteBuffer(4 + $length + $padding_length, $append . \danog\MadelineProto\Tools::random($padding_length));
|
||||
yield $buffer->bufferWrite(\pack('V', $padding_length + $length));
|
||||
|
||||
return $buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get read buffer asynchronously.
|
||||
*
|
||||
@ -88,10 +83,8 @@ class IntermediatePaddedStream implements BufferedStreamInterface, MTProtoBuffer
|
||||
{
|
||||
$buffer = yield $this->stream->getReadBuffer($l);
|
||||
$length = \unpack('V', yield $buffer->bufferRead(4))[1];
|
||||
|
||||
return $buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
@ -110,7 +103,6 @@ class IntermediatePaddedStream implements BufferedStreamInterface, MTProtoBuffer
|
||||
{
|
||||
return $this->stream;
|
||||
}
|
||||
|
||||
public static function getName(): string
|
||||
{
|
||||
return __CLASS__;
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* TCP Intermediate stream wrapper.
|
||||
*
|
||||
@ -24,7 +25,6 @@ use danog\MadelineProto\Stream\Async\BufferedStream;
|
||||
use danog\MadelineProto\Stream\BufferedStreamInterface;
|
||||
use danog\MadelineProto\Stream\ConnectionContext;
|
||||
use danog\MadelineProto\Stream\MTProtoBufferInterface;
|
||||
|
||||
use danog\MadelineProto\Stream\RawStreamInterface;
|
||||
|
||||
/**
|
||||
@ -38,7 +38,6 @@ class IntermediateStream implements BufferedStreamInterface, MTProtoBufferInterf
|
||||
{
|
||||
use BufferedStream;
|
||||
private $stream;
|
||||
|
||||
/**
|
||||
* Connect to stream.
|
||||
*
|
||||
@ -50,7 +49,6 @@ class IntermediateStream implements BufferedStreamInterface, MTProtoBufferInterf
|
||||
{
|
||||
$this->stream = yield $ctx->getStream(\str_repeat(\chr(238), 4) . $header);
|
||||
}
|
||||
|
||||
/**
|
||||
* Async close.
|
||||
*
|
||||
@ -60,7 +58,6 @@ class IntermediateStream implements BufferedStreamInterface, MTProtoBufferInterf
|
||||
{
|
||||
return $this->stream->disconnect();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get write buffer asynchronously.
|
||||
*
|
||||
@ -72,10 +69,8 @@ class IntermediateStream implements BufferedStreamInterface, MTProtoBufferInterf
|
||||
{
|
||||
$buffer = yield $this->stream->getWriteBuffer($length + 4, $append);
|
||||
yield $buffer->bufferWrite(\pack('V', $length));
|
||||
|
||||
return $buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get read buffer asynchronously.
|
||||
*
|
||||
@ -87,10 +82,8 @@ class IntermediateStream implements BufferedStreamInterface, MTProtoBufferInterf
|
||||
{
|
||||
$buffer = yield $this->stream->getReadBuffer($l);
|
||||
$length = \unpack('V', yield $buffer->bufferRead(4))[1];
|
||||
|
||||
return $buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
@ -109,8 +102,6 @@ class IntermediateStream implements BufferedStreamInterface, MTProtoBufferInterf
|
||||
{
|
||||
return $this->stream;
|
||||
}
|
||||
|
||||
|
||||
public static function getName(): string
|
||||
{
|
||||
return __CLASS__;
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Obfuscated2 stream wrapper.
|
||||
*
|
||||
@ -33,10 +34,8 @@ use danog\MadelineProto\Stream\ConnectionContext;
|
||||
class ObfuscatedStream extends CtrStream implements BufferedProxyStreamInterface
|
||||
{
|
||||
use Stream;
|
||||
|
||||
private $stream;
|
||||
private $extra;
|
||||
|
||||
/**
|
||||
* Connect to stream.
|
||||
*
|
||||
@ -50,50 +49,28 @@ class ObfuscatedStream extends CtrStream implements BufferedProxyStreamInterface
|
||||
$ctx = $ctx->getCtx();
|
||||
$ctx->setUri('tcp://' . $this->extra['address'] . ':' . $this->extra['port']);
|
||||
}
|
||||
|
||||
do {
|
||||
$random = \danog\MadelineProto\Tools::random(64);
|
||||
} while (\in_array(\substr($random, 0, 4), ['PVrG', 'GET ', 'POST', 'HEAD', \str_repeat(\chr(238), 4), \str_repeat(\chr(221), 4)]) || $random[0] === \chr(0xef) || \substr($random, 4, 4) === "\0\0\0\0");
|
||||
|
||||
if (\strlen($header) === 1) {
|
||||
$header = \str_repeat($header, 4);
|
||||
}
|
||||
$random = \substr_replace($random, $header . \substr($random, 56 + \strlen($header)), 56);
|
||||
$random = \substr_replace($random, \pack('s', $ctx->getIntDc()) . \substr($random, 60 + 2), 60);
|
||||
|
||||
$reversed = \strrev($random);
|
||||
|
||||
$key = \substr($random, 8, 32);
|
||||
$keyRev = \substr($reversed, 8, 32);
|
||||
|
||||
if (isset($this->extra['secret'])) {
|
||||
$key = \hash('sha256', $key . $this->extra['secret'], true);
|
||||
$keyRev = \hash('sha256', $keyRev . $this->extra['secret'], true);
|
||||
}
|
||||
|
||||
$iv = \substr($random, 40, 16);
|
||||
$ivRev = \substr($reversed, 40, 16);
|
||||
|
||||
parent::setExtra(
|
||||
[
|
||||
'encrypt' => [
|
||||
'key' => $key,
|
||||
'iv' => $iv
|
||||
],
|
||||
'decrypt' => [
|
||||
'key' => $keyRev,
|
||||
'iv' => $ivRev
|
||||
]
|
||||
]
|
||||
);
|
||||
parent::setExtra(['encrypt' => ['key' => $key, 'iv' => $iv], 'decrypt' => ['key' => $keyRev, 'iv' => $ivRev]]);
|
||||
yield from parent::connectGenerator($ctx);
|
||||
|
||||
$random = \substr_replace($random, \substr(@$this->getEncryptor()->encrypt($random), 56, 8), 56, 8);
|
||||
|
||||
yield $this->getStream()->write($random);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Does nothing.
|
||||
*
|
||||
@ -111,7 +88,6 @@ class ObfuscatedStream extends CtrStream implements BufferedProxyStreamInterface
|
||||
$extra['secret'] = \substr($extra['secret'], 1, 16);
|
||||
}
|
||||
}
|
||||
|
||||
$this->extra = $extra;
|
||||
}
|
||||
public static function getName(): string
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* HTTP proxy stream wrapper.
|
||||
*
|
||||
@ -25,7 +26,6 @@ use danog\MadelineProto\Stream\Async\RawStream;
|
||||
use danog\MadelineProto\Stream\BufferedProxyStreamInterface;
|
||||
use danog\MadelineProto\Stream\ConnectionContext;
|
||||
use danog\MadelineProto\Stream\RawProxyStreamInterface;
|
||||
|
||||
use danog\MadelineProto\Stream\RawStreamInterface;
|
||||
|
||||
/**
|
||||
@ -37,7 +37,6 @@ class HttpProxy implements RawProxyStreamInterface, BufferedProxyStreamInterface
|
||||
{
|
||||
use RawStream;
|
||||
private $extra;
|
||||
|
||||
/**
|
||||
* Connect to stream.
|
||||
*
|
||||
@ -50,33 +49,28 @@ class HttpProxy implements RawProxyStreamInterface, BufferedProxyStreamInterface
|
||||
$ctx = $ctx->getCtx();
|
||||
$uri = $ctx->getUri();
|
||||
$secure = $ctx->isSecure();
|
||||
|
||||
if ($secure) {
|
||||
$ctx->setSocketContext($ctx->getSocketContext()->withTlsContext(new ClientTlsContext($uri->getHost())));
|
||||
}
|
||||
|
||||
$ctx->setUri('tcp://' . $this->extra['address'] . ':' . $this->extra['port'])->secure(false);
|
||||
|
||||
$this->stream = yield $ctx->getStream();
|
||||
$address = $uri->getHost();
|
||||
$port = $uri->getPort();
|
||||
|
||||
try {
|
||||
if (\strlen(\inet_pton($address) === 16)) {
|
||||
$address = '[' . $address . ']';
|
||||
}
|
||||
} catch (\danog\MadelineProto\Exception $e) {
|
||||
}
|
||||
|
||||
yield $this->stream->write("CONNECT $address:$port HTTP/1.1\r\nHost: $address:$port\r\nAccept: */*\r\n".$this->getProxyAuthHeader()."Connection: keep-Alive\r\n\r\n");
|
||||
|
||||
yield $this->stream->write("CONNECT {$address}:{$port} HTTP/1.1\r\nHost: {$address}:{$port}\r\nAccept: */*\r\n" . $this->getProxyAuthHeader() . "Connection: keep-Alive\r\n\r\n");
|
||||
$buffer = yield $this->stream->getReadBuffer($l);
|
||||
$headers = '';
|
||||
$was_crlf = false;
|
||||
while (true) {
|
||||
$piece = yield $buffer->bufferRead(2);
|
||||
$headers .= $piece;
|
||||
if ($piece === "\n\r") { // Assume end of headers with \r\n\r\n
|
||||
if ($piece === "\n\r") {
|
||||
// Assume end of headers with \r\n\r\n
|
||||
$headers .= yield $buffer->bufferRead(1);
|
||||
break;
|
||||
}
|
||||
@ -86,7 +80,6 @@ class HttpProxy implements RawProxyStreamInterface, BufferedProxyStreamInterface
|
||||
$was_crlf = $piece === "\r\n";
|
||||
}
|
||||
$headers = \explode("\r\n", $headers);
|
||||
|
||||
list($protocol, $code, $description) = \explode(' ', $headers[0], 3);
|
||||
list($protocol, $protocol_version) = \explode('/', $protocol);
|
||||
if ($protocol !== 'HTTP') {
|
||||
@ -102,28 +95,22 @@ class HttpProxy implements RawProxyStreamInterface, BufferedProxyStreamInterface
|
||||
$current_header = \explode(':', $current_header, 2);
|
||||
$headers[\strtolower($current_header[0])] = \trim($current_header[1]);
|
||||
}
|
||||
|
||||
$close = $protocol === 'HTTP/1.0';
|
||||
if (isset($headers['connection'])) {
|
||||
$close = \strtolower($headers['connection']) === 'close';
|
||||
}
|
||||
|
||||
if ($code !== 200) {
|
||||
$read = '';
|
||||
if (isset($headers['content-length'])) {
|
||||
$read = yield $buffer->bufferRead((int) $headers['content-length']);
|
||||
}
|
||||
|
||||
if ($close) {
|
||||
$this->disconnect();
|
||||
yield $this->connect($ctx);
|
||||
}
|
||||
|
||||
\danog\MadelineProto\Logger::log(\trim($read));
|
||||
|
||||
throw new \danog\MadelineProto\Exception($description, $code);
|
||||
}
|
||||
|
||||
if ($close) {
|
||||
yield $this->stream->disconnect();
|
||||
yield $this->stream->connect($ctx);
|
||||
@ -132,17 +119,14 @@ class HttpProxy implements RawProxyStreamInterface, BufferedProxyStreamInterface
|
||||
$length = (int) $headers['content-length'];
|
||||
$read = yield $buffer->bufferRead($length);
|
||||
}
|
||||
|
||||
if ($secure) {
|
||||
yield $this->getSocket()->setupTls();
|
||||
}
|
||||
\danog\MadelineProto\Logger::log('Connected to ' . $address . ':' . $port . ' via http');
|
||||
|
||||
if (\strlen($header)) {
|
||||
yield (yield $this->stream->getWriteBuffer(\strlen($header)))->bufferWrite($header);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Async close.
|
||||
*
|
||||
@ -152,7 +136,6 @@ class HttpProxy implements RawProxyStreamInterface, BufferedProxyStreamInterface
|
||||
{
|
||||
return $this->stream->disconnect();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get write buffer asynchronously.
|
||||
*
|
||||
@ -164,7 +147,6 @@ class HttpProxy implements RawProxyStreamInterface, BufferedProxyStreamInterface
|
||||
{
|
||||
return $this->stream->getWriteBuffer($length, $append);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get read buffer asynchronously.
|
||||
*
|
||||
@ -176,26 +158,21 @@ class HttpProxy implements RawProxyStreamInterface, BufferedProxyStreamInterface
|
||||
{
|
||||
return $this->stream->getReadBuffer($length);
|
||||
}
|
||||
|
||||
public function read(): Promise
|
||||
{
|
||||
return $this->stream->read();
|
||||
}
|
||||
|
||||
public function write(string $data): Promise
|
||||
{
|
||||
return $this->stream->write($data);
|
||||
}
|
||||
|
||||
private function getProxyAuthHeader()
|
||||
{
|
||||
if (!isset($this->extra['username']) || !isset($this->extra['password'])) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return 'Proxy-Authorization: Basic ' . \base64_encode($this->extra['username'] . ':' . $this->extra['password']) . "\r\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets proxy data.
|
||||
*
|
||||
@ -207,7 +184,6 @@ class HttpProxy implements RawProxyStreamInterface, BufferedProxyStreamInterface
|
||||
{
|
||||
$this->extra = $extra;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
@ -217,7 +193,6 @@ class HttpProxy implements RawProxyStreamInterface, BufferedProxyStreamInterface
|
||||
{
|
||||
return $this->stream->getSocket();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Socks5 stream wrapper.
|
||||
*
|
||||
@ -25,7 +26,6 @@ use danog\MadelineProto\Stream\Async\RawStream;
|
||||
use danog\MadelineProto\Stream\BufferedProxyStreamInterface;
|
||||
use danog\MadelineProto\Stream\ConnectionContext;
|
||||
use danog\MadelineProto\Stream\RawProxyStreamInterface;
|
||||
|
||||
use danog\MadelineProto\Stream\RawStreamInterface;
|
||||
|
||||
/**
|
||||
@ -35,20 +35,9 @@ use danog\MadelineProto\Stream\RawStreamInterface;
|
||||
*/
|
||||
class SocksProxy implements RawProxyStreamInterface, BufferedProxyStreamInterface
|
||||
{
|
||||
const REPS = [
|
||||
0 => 'succeeded',
|
||||
1 => 'general SOCKS server failure',
|
||||
2 => 'connection not allowed by ruleset',
|
||||
3 => 'Network unreachable',
|
||||
4 => 'Host unreachable',
|
||||
5 => 'Connection refused',
|
||||
6 => 'TTL expired',
|
||||
7 => 'Command not supported',
|
||||
8 => 'Address type not supported'
|
||||
];
|
||||
const REPS = [0 => 'succeeded', 1 => 'general SOCKS server failure', 2 => 'connection not allowed by ruleset', 3 => 'Network unreachable', 4 => 'Host unreachable', 5 => 'Connection refused', 6 => 'TTL expired', 7 => 'Command not supported', 8 => 'Address type not supported'];
|
||||
use RawStream;
|
||||
private $extra;
|
||||
|
||||
/**
|
||||
* Connect to stream.
|
||||
*
|
||||
@ -61,78 +50,61 @@ class SocksProxy implements RawProxyStreamInterface, BufferedProxyStreamInterfac
|
||||
$ctx = $ctx->getCtx();
|
||||
$uri = $ctx->getUri();
|
||||
$secure = $ctx->isSecure();
|
||||
|
||||
if ($secure) {
|
||||
$ctx->setSocketContext($ctx->getSocketContext()->withTlsContext(new ClientTlsContext($uri->getHost())));
|
||||
}
|
||||
|
||||
$ctx->setUri('tcp://' . $this->extra['address'] . ':' . $this->extra['port'])->secure(false);
|
||||
|
||||
$methods = \chr(0);
|
||||
if (isset($this->extra['username']) && isset($this->extra['password'])) {
|
||||
$methods .= \chr(2);
|
||||
}
|
||||
$this->stream = yield $ctx->getStream(\chr(5) . \chr(\strlen($methods)) . $methods);
|
||||
|
||||
$l = 2;
|
||||
|
||||
$buffer = yield $this->stream->getReadBuffer($l);
|
||||
|
||||
$version = \ord(yield $buffer->bufferRead(1));
|
||||
$method = \ord(yield $buffer->bufferRead(1));
|
||||
|
||||
if ($version !== 5) {
|
||||
throw new \danog\MadelineProto\Exception("Wrong SOCKS5 version: $version");
|
||||
throw new \danog\MadelineProto\Exception("Wrong SOCKS5 version: {$version}");
|
||||
}
|
||||
if ($method === 2) {
|
||||
$auth = \chr(1) . \chr(\strlen($this->extra['username'])) . $this->extra['username'] . \chr(\strlen($this->extra['password'])) . $this->extra['password'];
|
||||
yield $this->stream->write($auth);
|
||||
|
||||
$buffer = yield $this->stream->getReadBuffer($l);
|
||||
|
||||
$version = \ord(yield $buffer->bufferRead(1));
|
||||
$result = \ord(yield $buffer->bufferRead(1));
|
||||
|
||||
if ($version !== 1) {
|
||||
throw new \danog\MadelineProto\Exception("Wrong authorized SOCKS version: $version");
|
||||
throw new \danog\MadelineProto\Exception("Wrong authorized SOCKS version: {$version}");
|
||||
}
|
||||
if ($result !== 0) {
|
||||
throw new \danog\MadelineProto\Exception("Wrong authorization status: $version");
|
||||
throw new \danog\MadelineProto\Exception("Wrong authorization status: {$version}");
|
||||
}
|
||||
} elseif ($method !== 0) {
|
||||
throw new \danog\MadelineProto\Exception("Wrong method: $method");
|
||||
throw new \danog\MadelineProto\Exception("Wrong method: {$method}");
|
||||
}
|
||||
$payload = \pack('C3', 0x05, 0x01, 0x00);
|
||||
|
||||
$payload = \pack('C3', 0x5, 0x1, 0x0);
|
||||
try {
|
||||
$ip = \inet_pton($uri->getHost());
|
||||
$payload .= $ip ? \pack('C1', \strlen($ip) === 4 ? 0x01 : 0x04).$ip : \pack('C2', 0x03, \strlen($uri->getHost())).$uri->getHost();
|
||||
$payload .= $ip ? \pack('C1', \strlen($ip) === 4 ? 0x1 : 0x4) . $ip : \pack('C2', 0x3, \strlen($uri->getHost())) . $uri->getHost();
|
||||
} catch (\danog\MadelineProto\Exception $e) {
|
||||
$payload .= \pack('C2', 0x03, \strlen($uri->getHost())).$uri->getHost();
|
||||
$payload .= \pack('C2', 0x3, \strlen($uri->getHost())) . $uri->getHost();
|
||||
}
|
||||
|
||||
$payload .= \pack('n', $uri->getPort());
|
||||
yield $this->stream->write($payload);
|
||||
|
||||
$l = 4;
|
||||
$buffer = yield $this->stream->getReadBuffer($l);
|
||||
|
||||
$version = \ord(yield $buffer->bufferRead(1));
|
||||
if ($version !== 5) {
|
||||
throw new \danog\MadelineProto\Exception("Wrong SOCKS5 version: $version");
|
||||
throw new \danog\MadelineProto\Exception("Wrong SOCKS5 version: {$version}");
|
||||
}
|
||||
|
||||
$rep = \ord(yield $buffer->bufferRead(1));
|
||||
if ($rep !== 0) {
|
||||
$rep = self::REPS[$rep] ?? $rep;
|
||||
throw new \danog\MadelineProto\Exception("Wrong SOCKS5 rep: $rep");
|
||||
throw new \danog\MadelineProto\Exception("Wrong SOCKS5 rep: {$rep}");
|
||||
}
|
||||
|
||||
$rsv = \ord(yield $buffer->bufferRead(1));
|
||||
if ($rsv !== 0) {
|
||||
throw new \danog\MadelineProto\Exception("Wrong socks5 final RSV: $rsv");
|
||||
throw new \danog\MadelineProto\Exception("Wrong socks5 final RSV: {$rsv}");
|
||||
}
|
||||
|
||||
switch (\ord(yield $buffer->bufferRead(1))) {
|
||||
case 1:
|
||||
$buffer = yield $this->stream->getReadBuffer($l);
|
||||
@ -147,7 +119,6 @@ class SocksProxy implements RawProxyStreamInterface, BufferedProxyStreamInterfac
|
||||
$l = 1;
|
||||
$buffer = yield $this->stream->getReadBuffer($l);
|
||||
$length = \ord(yield $buffer->bufferRead(1));
|
||||
|
||||
$buffer = yield $this->stream->getReadBuffer($length);
|
||||
$ip = yield $buffer->bufferRead($length);
|
||||
break;
|
||||
@ -155,9 +126,7 @@ class SocksProxy implements RawProxyStreamInterface, BufferedProxyStreamInterfac
|
||||
$l = 2;
|
||||
$buffer = yield $this->stream->getReadBuffer($l);
|
||||
$port = \unpack('n', yield $buffer->bufferRead(2))[1];
|
||||
|
||||
\danog\MadelineProto\Logger::log(['Connected to ' . $ip . ':' . $port . ' via socks5']);
|
||||
|
||||
if ($secure) {
|
||||
yield $this->getSocket()->setupTls();
|
||||
}
|
||||
@ -165,7 +134,6 @@ class SocksProxy implements RawProxyStreamInterface, BufferedProxyStreamInterfac
|
||||
yield (yield $this->stream->getWriteBuffer(\strlen($header)))->bufferWrite($header);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Async close.
|
||||
*
|
||||
@ -175,7 +143,6 @@ class SocksProxy implements RawProxyStreamInterface, BufferedProxyStreamInterfac
|
||||
{
|
||||
return $this->stream->disconnect();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get write buffer asynchronously.
|
||||
*
|
||||
@ -187,7 +154,6 @@ class SocksProxy implements RawProxyStreamInterface, BufferedProxyStreamInterfac
|
||||
{
|
||||
return $this->stream->getWriteBuffer($length, $append);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get read buffer asynchronously.
|
||||
*
|
||||
@ -199,17 +165,14 @@ class SocksProxy implements RawProxyStreamInterface, BufferedProxyStreamInterfac
|
||||
{
|
||||
return $this->stream->getReadBuffer($length);
|
||||
}
|
||||
|
||||
public function read(): Promise
|
||||
{
|
||||
return $this->stream->read();
|
||||
}
|
||||
|
||||
public function write(string $data): Promise
|
||||
{
|
||||
return $this->stream->write($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets proxy data.
|
||||
*
|
||||
@ -230,7 +193,6 @@ class SocksProxy implements RawProxyStreamInterface, BufferedProxyStreamInterfac
|
||||
{
|
||||
return $this->stream;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
@ -240,7 +202,6 @@ class SocksProxy implements RawProxyStreamInterface, BufferedProxyStreamInterfac
|
||||
{
|
||||
return $this->stream->getSocket();
|
||||
}
|
||||
|
||||
public static function getName(): string
|
||||
{
|
||||
return __CLASS__;
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Generic stream proxy interface.
|
||||
*
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Raw stream proxy interface.
|
||||
*
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user