Refactoring and encapsulation

This commit is contained in:
Daniil Gentili 2019-10-31 20:48:06 +01:00
parent d8dc41e6c3
commit 2720c50ca0
Signed by: danog
GPG Key ID: 8C1BE3B34B230CA7
22 changed files with 668 additions and 310 deletions

View File

@ -20,17 +20,23 @@
namespace danog\MadelineProto; namespace danog\MadelineProto;
use Amp\Promise; use Amp\Promise;
use danog\MadelineProto\TL\TL;
use phpDocumentor\Reflection\DocBlockFactory; use phpDocumentor\Reflection\DocBlockFactory;
class AnnotationsBuilder class AnnotationsBuilder
{ {
use \danog\MadelineProto\TL\TL;
use Tools; use Tools;
public function __construct($logger, $settings) public function __construct($logger, $settings)
{ {
$this->logger = $logger; $this->logger = $logger;
$this->constructTL($settings['tl_schema']); $this->TL = new TL(new class($logger) {
public function __construct($logger)
{
$this->logger = $logger;
}
});
$this->TL->init($settings['tl_schema']);
$this->settings = $settings; $this->settings = $settings;
} }
@ -62,7 +68,7 @@ class AnnotationsBuilder
} }
} }
} }
foreach ($this->getMethodNamespaces() as $namespace) { foreach ($this->TL->getMethodNamespaces() as $namespace) {
$content = \preg_replace('/(class( \\w+[,]?){0,}\\n{\\n)/', '${1}'." /**\n"." * @internal this is a internal property generated by build_docs.php, don't change manually\n"." *\n"." * @var {$namespace}\n"." */\n"." public \${$namespace};\n", $content); $content = \preg_replace('/(class( \\w+[,]?){0,}\\n{\\n)/', '${1}'." /**\n"." * @internal this is a internal property generated by build_docs.php, don't change manually\n"." *\n"." * @var {$namespace}\n"." */\n"." public \${$namespace};\n", $content);
} }
\file_put_contents($filename, $content); \file_put_contents($filename, $content);
@ -87,12 +93,12 @@ class AnnotationsBuilder
$handle = \fopen(\dirname(__FILE__).'/InternalDoc.php', 'w'); $handle = \fopen(\dirname(__FILE__).'/InternalDoc.php', 'w');
$internalDoc = []; $internalDoc = [];
foreach ($this->methods->by_id as $id => $data) { foreach ($this->TL->getMethods()->by_id as $id => $data) {
if (!\strpos($data['method'], '.')) { if (!\strpos($data['method'], '.')) {
continue; continue;
} }
list($namespace, $method) = \explode('.', $data['method']); list($namespace, $method) = \explode('.', $data['method']);
if (!\in_array($namespace, $this->getMethodNamespaces())) { if (!\in_array($namespace, $this->TL->getMethodNamespaces())) {
continue; continue;
} }
$internalDoc[$namespace][$method]['title'] = Lang::$current_lang["method_{$data['method']}"] ?? ''; $internalDoc[$namespace][$method]['title'] = Lang::$current_lang["method_{$data['method']}"] ?? '';

View File

@ -439,9 +439,9 @@ class Connection extends Session
} }
if ($message['method']) { if ($message['method']) {
$body = yield $this->API->serializeMethod($message['_'], $body); $body = yield $this->API->getTL()->serializeMethod($message['_'], $body);
} else { } else {
$body = yield $this->API->serializeObject(['type' => $message['_']], $body, $message['_']); $body = yield $this->API->getTL()->serializeObject(['type' => $message['_']], $body, $message['_']);
} }
if ($refreshNext) { if ($refreshNext) {
$this->API->referenceDatabase->refreshNext(false); $this->API->referenceDatabase->refreshNext(false);

View File

@ -19,10 +19,11 @@
namespace danog\MadelineProto; namespace danog\MadelineProto;
use danog\MadelineProto\TL\TL;
// This code was written a few years ago: it is garbage, and has to be rewritten // This code was written a few years ago: it is garbage, and has to be rewritten
class DocsBuilder class DocsBuilder
{ {
use \danog\MadelineProto\TL\TL;
use \danog\MadelineProto\DocsBuilder\Methods; use \danog\MadelineProto\DocsBuilder\Methods;
use \danog\MadelineProto\DocsBuilder\Constructors; use \danog\MadelineProto\DocsBuilder\Constructors;
use Tools; use Tools;
@ -32,10 +33,14 @@ class DocsBuilder
{ {
$this->logger = $logger; $this->logger = $logger;
\set_error_handler(['\\danog\\MadelineProto\\Exception', 'ExceptionErrorHandler']); \set_error_handler(['\\danog\\MadelineProto\\Exception', 'ExceptionErrorHandler']);
$this->constructTL($settings['tl_schema']); $this->TL = new TL(new class($logger) {
public function __construct($logger)
{
$this->logger = $logger;
}
});
$this->TL->init($settings['tl_schema']);
if (isset($settings['tl_schema']['td']) && !isset($settings['tl_schema']['telegram'])) { if (isset($settings['tl_schema']['td']) && !isset($settings['tl_schema']['telegram'])) {
$this->constructors = $this->td_constructors;
$this->methods = $this->td_methods;
$this->td = true; $this->td = true;
} }
$this->settings = $settings; $this->settings = $settings;

View File

@ -35,15 +35,15 @@ trait Constructors
$this->docs_constructors = []; $this->docs_constructors = [];
$this->logger->logger('Generating constructors documentation...', \danog\MadelineProto\Logger::NOTICE); $this->logger->logger('Generating constructors documentation...', \danog\MadelineProto\Logger::NOTICE);
$got = []; $got = [];
foreach ($this->constructors->by_predicate_and_layer as $predicate => $id) { foreach ($this->TL->getConstructors($this->td)->by_predicate_and_layer as $predicate => $id) {
$data = $this->constructors->by_id[$id]; $data = $this->TL->getConstructors($this->td)->by_id[$id];
if (isset($got[$id])) { if (isset($got[$id])) {
$data['layer'] = ''; $data['layer'] = '';
} }
$got[$id] = ''; $got[$id] = '';
/* /*
if (preg_match('/%/', $type)) { if (preg_match('/%/', $type)) {
$type = $this->constructors->findByType(str_replace('%', '', $type))['predicate']; $type = $this->TL->getConstructors($this->td)->findByType(str_replace('%', '', $type))['predicate'];
}*/ }*/
$layer = isset($data['layer']) && $data['layer'] !== '' ? '_'.$data['layer'] : ''; $layer = isset($data['layer']) && $data['layer'] !== '' ? '_'.$data['layer'] : '';
$type = \str_replace(['.', '<', '>'], ['_', '_of_', ''], $data['type']); $type = \str_replace(['.', '<', '>'], ['_', '_of_', ''], $data['type']);
@ -69,7 +69,7 @@ trait Constructors
$type_or_bare_type = \ctype_upper(Tools::end(\explode('.', $param[$type_or_subtype]))[0]) || \in_array($param[$type_or_subtype], ['!X', 'X', 'bytes', 'true', 'false', 'double', 'string', 'Bool', 'int53', 'int', 'long', 'int128', 'int256', 'int512']) ? 'types' : 'constructors'; $type_or_bare_type = \ctype_upper(Tools::end(\explode('.', $param[$type_or_subtype]))[0]) || \in_array($param[$type_or_subtype], ['!X', 'X', 'bytes', 'true', 'false', 'double', 'string', 'Bool', 'int53', 'int', 'long', 'int128', 'int256', 'int512']) ? 'types' : 'constructors';
$param[$type_or_subtype] = \str_replace(['.', 'true', 'false'], ['_', 'Bool', 'Bool'], $param[$type_or_subtype]); $param[$type_or_subtype] = \str_replace(['.', 'true', 'false'], ['_', 'Bool', 'Bool'], $param[$type_or_subtype]);
if (\preg_match('/%/', $param[$type_or_subtype])) { if (\preg_match('/%/', $param[$type_or_subtype])) {
$param[$type_or_subtype] = $this->constructors->findByType(\str_replace('%', '', $param[$type_or_subtype]))['predicate']; $param[$type_or_subtype] = $this->TL->getConstructors($this->td)->findByType(\str_replace('%', '', $param[$type_or_subtype]))['predicate'];
} }
if (\substr($param[$type_or_subtype], -1) === '>') { if (\substr($param[$type_or_subtype], -1) === '>') {
$param[$type_or_subtype] = \substr($param[$type_or_subtype], 0, -1); $param[$type_or_subtype] = \substr($param[$type_or_subtype], 0, -1);
@ -87,14 +87,14 @@ trait Constructors
| Name | Type | Required | | Name | Type | Required |
|----------|---------------|----------| |----------|---------------|----------|
'; ';
if (!isset($this->td_descriptions['constructors'][$data['predicate']])) { if (!isset($this->TL->getDescriptions()['constructors'][$data['predicate']])) {
$this->addToLang('object_'.$data['predicate']); $this->addToLang('object_'.$data['predicate']);
if (\danog\MadelineProto\Lang::$lang['en']['object_'.$data['predicate']] !== '') { if (\danog\MadelineProto\Lang::$lang['en']['object_'.$data['predicate']] !== '') {
$this->td_descriptions['constructors'][$data['predicate']]['description'] = \danog\MadelineProto\Lang::$lang['en']['object_'.$data['predicate']]; $this->TL->getDescriptions()['constructors'][$data['predicate']]['description'] = \danog\MadelineProto\Lang::$lang['en']['object_'.$data['predicate']];
} }
} }
if (isset($this->td_descriptions['constructors'][$data['predicate']]) && !empty($data['params'])) { if (isset($this->TL->getDescriptions()['constructors'][$data['predicate']]) && !empty($data['params'])) {
$table = '### Attributes: $table = '### Attributes:
| Name | Type | Required | Description | | Name | Type | Required | Description |
@ -126,7 +126,7 @@ trait Constructors
} }
}*/ }*/
if (\preg_match('/%/', $ptype)) { if (\preg_match('/%/', $ptype)) {
$ptype = $this->constructors->findByType(\str_replace('%', '', $ptype))['predicate']; $ptype = $this->TL->getConstructors($this->td)->findByType(\str_replace('%', '', $ptype))['predicate'];
} }
$type_or_bare_type = (\ctype_upper(Tools::end(\explode('_', $ptype))[0]) || \in_array($ptype, ['!X', 'X', 'bytes', 'true', 'false', 'double', 'string', 'Bool', 'int53', 'int', 'long', 'int128', 'int256', 'int512'])) && $ptype !== 'MTmessage' ? 'types' : 'constructors'; $type_or_bare_type = (\ctype_upper(Tools::end(\explode('_', $ptype))[0]) || \in_array($ptype, ['!X', 'X', 'bytes', 'true', 'false', 'double', 'string', 'Bool', 'int53', 'int', 'long', 'int128', 'int256', 'int512'])) && $ptype !== 'MTmessage' ? 'types' : 'constructors';
if (\substr($ptype, -1) === '>') { if (\substr($ptype, -1) === '>') {
@ -156,16 +156,16 @@ trait Constructors
if (\in_array($ptype, ['InputEncryptedFile']) && !isset($this->settings['td'])) { if (\in_array($ptype, ['InputEncryptedFile']) && !isset($this->settings['td'])) {
$human_ptype = 'File path or '.$ptype; $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->constructors->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->td_descriptions['constructors'][$data['predicate']]['params'][$param['name']])) { if (!isset($this->TL->getDescriptions()['constructors'][$data['predicate']]['params'][$param['name']])) {
$this->addToLang('object_'.$data['predicate'].'_param_'.$param['name'].'_type_'.$param['type']); $this->addToLang('object_'.$data['predicate'].'_param_'.$param['name'].'_type_'.$param['type']);
if (isset($this->td_descriptions['constructors'][$data['predicate']]['description'])) { if (isset($this->TL->getDescriptions()['constructors'][$data['predicate']]['description'])) {
$this->td_descriptions['constructors'][$data['predicate']]['params'][$param['name']] = \danog\MadelineProto\Lang::$lang['en']['object_'.$data['predicate'].'_param_'.$param['name'].'_type_'.$param['type']]; $this->TL->getDescriptions()['constructors'][$data['predicate']]['params'][$param['name']] = \danog\MadelineProto\Lang::$lang['en']['object_'.$data['predicate'].'_param_'.$param['name'].'_type_'.$param['type']];
} }
} }
if (isset($this->td_descriptions['constructors'][$data['predicate']]['params'][$param['name']])) { if (isset($this->TL->getDescriptions()['constructors'][$data['predicate']]['params'][$param['name']])) {
$table .= $this->td_descriptions['constructors'][$data['predicate']]['params'][$param['name']].'|'; $table .= $this->TL->getDescriptions()['constructors'][$data['predicate']]['params'][$param['name']].'|';
} }
$table .= PHP_EOL; $table .= PHP_EOL;
$pptype = \in_array($ptype, ['string', 'bytes']) ? "'".$ptype."'" : $ptype; $pptype = \in_array($ptype, ['string', 'bytes']) ? "'".$ptype."'" : $ptype;
@ -183,7 +183,7 @@ trait Constructors
$params = "['_' => '".$data['predicate']."'".$params.']'; $params = "['_' => '".$data['predicate']."'".$params.']';
$lua_params = "{_='".$data['predicate']."'".$lua_params.'}'; $lua_params = "{_='".$data['predicate']."'".$lua_params.'}';
$pwr_params = '{"_": "'.$data['predicate'].'"'.$pwr_params.'}'; $pwr_params = '{"_": "'.$data['predicate'].'"'.$pwr_params.'}';
$description = isset($this->td_descriptions['constructors'][$data['predicate']]) ? $this->td_descriptions['constructors'][$data['predicate']]['description'] : $constructor.' attributes, type and example'; $description = isset($this->TL->getDescriptions()['constructors'][$data['predicate']]) ? $this->TL->getDescriptions()['constructors'][$data['predicate']]['description'] : $constructor.' attributes, type and example';
$header = '--- $header = '---
title: '.$data['predicate'].' title: '.$data['predicate'].'
description: '.$description.' description: '.$description.'
@ -199,8 +199,8 @@ image: https://docs.madelineproto.xyz/favicons/android-chrome-256x256.png
'; ';
if (isset($this->td_descriptions['constructors'][$data['predicate']])) { if (isset($this->TL->getDescriptions()['constructors'][$data['predicate']])) {
$header .= $this->td_descriptions['constructors'][$data['predicate']]['description'].PHP_EOL.PHP_EOL; $header .= $this->TL->getDescriptions()['constructors'][$data['predicate']]['description'].PHP_EOL.PHP_EOL;
} }
$type = '### Type: ['.\str_replace('_', '\\_', $php_type).'](../types/'.$php_type.'.md) $type = '### Type: ['.\str_replace('_', '\\_', $php_type).'](../types/'.$php_type.'.md)

View File

@ -54,7 +54,7 @@ trait Methods
$this->docs_methods = []; $this->docs_methods = [];
$this->human_docs_methods = []; $this->human_docs_methods = [];
$this->logger->logger('Generating methods documentation...', \danog\MadelineProto\Logger::NOTICE); $this->logger->logger('Generating methods documentation...', \danog\MadelineProto\Logger::NOTICE);
foreach ($this->methods->by_id as $id => $data) { foreach ($this->TL->getMethods($this->td)->by_id as $id => $data) {
$method = $data['method']; $method = $data['method'];
$php_method = \str_replace('.', '->', $data['method']); $php_method = \str_replace('.', '->', $data['method']);
$type = \str_replace(['.', '<', '>'], ['_', '_of_', ''], $data['type']); $type = \str_replace(['.', '<', '>'], ['_', '_of_', ''], $data['type']);
@ -170,9 +170,9 @@ trait Methods
} }
if (isset($this->td_descriptions['methods'][$data['method']])) { 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->constructors->findByPredicate(\lcfirst($param['type']).'Empty')) && $id['type'] === $param['type']) || (($id = $this->constructors->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 { } 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->constructors->findByPredicate(\lcfirst($param['type']).'Empty')) && $id['type'] === $param['type']) || (($id = $this->constructors->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; $table .= PHP_EOL;
$pptype = \in_array($ptype, ['string', 'bytes']) ? "'".$ptype."'" : $ptype; $pptype = \in_array($ptype, ['string', 'bytes']) ? "'".$ptype."'" : $ptype;

View File

@ -20,12 +20,12 @@
namespace danog\MadelineProto; namespace danog\MadelineProto;
/** /**
* Event handler * Event handler.
*/ */
class EventHandler extends InternalDoc class EventHandler extends InternalDoc
{ {
/** /**
* Constructor * Constructor.
* *
* @param API|null $MadelineProto MadelineProto instance * @param API|null $MadelineProto MadelineProto instance
*/ */

View File

@ -4040,6 +4040,33 @@ class InternalDoc extends APIFactory
{ {
return $this->__call(__FUNCTION__, [$param, $level, $file, $extra]); return $this->__call(__FUNCTION__, [$param, $level, $file, $extra]);
} }
/**
* Get TL namespaces.
*
* @return array
*/
public function getMethodNamespaces(array $extra = []): array
{
return $this->__call(__FUNCTION__, [$extra]);
}
/**
* Get namespaced methods (method => namespace).
*
* @return array
*/
public function getMethodsNamespaced(array $extra = []): array
{
return $this->__call(__FUNCTION__, [$extra]);
}
/**
* Get TL serializer.
*
* @return TL
*/
public function getTL(array $extra = []): danog\MadelineProto\TL\TL
{
return $this->__call(__FUNCTION__, [$extra]);
}
/** /**
* Get async HTTP client. * Get async HTTP client.
* *
@ -4078,11 +4105,6 @@ class InternalDoc extends APIFactory
{ {
return $this->__call(__FUNCTION__, [$extra]); return $this->__call(__FUNCTION__, [$extra]);
} }
public function hasAllAuth(array $extra = []): bool
{
return $this->__call(__FUNCTION__, [$extra]);
}
/** /**
* Get correct settings array for the latest version. * Get correct settings array for the latest version.
* *
@ -4139,7 +4161,20 @@ class InternalDoc extends APIFactory
{ {
return $this->__call(__FUNCTION__, [$datacenter, $extra]); return $this->__call(__FUNCTION__, [$datacenter, $extra]);
} }
/**
* Checks whether all datacenters are authorized.
*
* @return boolean
*/
public function hasAllAuth(array $extra = []): bool
{
return $this->__call(__FUNCTION__, [$extra]);
}
/**
* Whether we're initing authorization.
*
* @return boolean
*/
public function isInitingAuthorization(array $extra = []) public function isInitingAuthorization(array $extra = [])
{ {
return $this->__call(__FUNCTION__, [$extra]); return $this->__call(__FUNCTION__, [$extra]);
@ -4689,57 +4724,79 @@ class InternalDoc extends APIFactory
{ {
return $this->__call(__FUNCTION__, [$message_media, $callable, $cb, $parallelize, $offset, $end, $part_size, $extra]); return $this->__call(__FUNCTION__, [$message_media, $callable, $cb, $parallelize, $offset, $end, $part_size, $extra]);
} }
/**
* Accept secret chat.
*
* @param array $params Secret chat ID
*
* @return \Generator
*/
public function acceptSecretChat($params, array $extra = []) public function acceptSecretChat($params, array $extra = [])
{ {
return $this->__call(__FUNCTION__, [$params, $extra]); return $this->__call(__FUNCTION__, [$params, $extra]);
} }
/**
* Request secret chat.
*
* @param mixed $user User to start secret chat with
*
* @return \Generator
*/
public function requestSecretChat($user, array $extra = []) public function requestSecretChat($user, array $extra = [])
{ {
return $this->__call(__FUNCTION__, [$user, $extra]); return $this->__call(__FUNCTION__, [$user, $extra]);
} }
/**
public function completeSecretChat($params, array $extra = []) * Rekey secret chat.
{ *
return $this->__call(__FUNCTION__, [$params, $extra]); * @param mixed $chat Secret chat to rekey
} *
* @return \Generator
public function notifyLayer($chat, array $extra = []) */
{
return $this->__call(__FUNCTION__, [$chat, $extra]);
}
public function rekey($chat, array $extra = []) public function rekey($chat, array $extra = [])
{ {
return $this->__call(__FUNCTION__, [$chat, $extra]); return $this->__call(__FUNCTION__, [$chat, $extra]);
} }
/**
public function acceptRekey($chat, $params, array $extra = []) * Get secret chat status.
{ *
return $this->__call(__FUNCTION__, [$chat, $params, $extra]); * @param int $chat Chat ID
} *
* @return int One of MTProto::SECRET_EMPTY, MTProto::SECRET_REQUESTED, MTProto::SECRET_READY
public function commitRekey($chat, $params, array $extra = []) */
{ public function secretChatStatus(int $chat, array $extra = []): int
return $this->__call(__FUNCTION__, [$chat, $params, $extra]);
}
public function completeRekey($chat, $params, array $extra = [])
{
return $this->__call(__FUNCTION__, [$chat, $params, $extra]);
}
public function secretChatStatus($chat, array $extra = [])
{ {
return $this->__call(__FUNCTION__, [$chat, $extra]); return $this->__call(__FUNCTION__, [$chat, $extra]);
} }
/**
public function getSecretChat($chat, array $extra = []) * Get secret chat.
*
* @param array|int $chat Secret chat ID
*
* @return array
*/
public function getSecretChat($chat, array $extra = []): array
{ {
return $this->__call(__FUNCTION__, [$chat, $extra]); return $this->__call(__FUNCTION__, [$chat, $extra]);
} }
/**
* Check whether secret chat exists.
*
* @param array|int $chat Secret chat ID
*
* @return boolean
*/
public function hasSecretChat($chat, array $extra = []): bool
{
return $this->__call(__FUNCTION__, [$chat, $extra]);
}
/**
* Discard secret chat.
*
* @param array|int $chat Secret chat ID
*
* @return \Generator
*/
public function discardSecretChat($chat, array $extra = []) public function discardSecretChat($chat, array $extra = [])
{ {
return $this->__call(__FUNCTION__, [$chat, $extra]); return $this->__call(__FUNCTION__, [$chat, $extra]);
@ -4790,63 +4847,6 @@ class InternalDoc extends APIFactory
return $this->__call(__FUNCTION__, [$chat, $extra]); return $this->__call(__FUNCTION__, [$chat, $extra]);
} }
public function constructTL($files, $objects = [
], array $extra = [])
{
return $this->__call(__FUNCTION__, [$files, $objects, $extra]);
}
public function getMethodNamespaces(array $extra = [])
{
return $this->__call(__FUNCTION__, [$extra]);
}
public function getMethodsNamespaced(array $extra = [])
{
return $this->__call(__FUNCTION__, [$extra]);
}
public function updateCallbacks($objects, array $extra = [])
{
return $this->__call(__FUNCTION__, [$objects, $extra]);
}
public function deserializeBool($id, array $extra = [])
{
return $this->__call(__FUNCTION__, [$id, $extra]);
}
public function serializeObject($type, $object, $ctx, $layer = -1, array $extra = [])
{
return $this->__call(__FUNCTION__, [$type, $object, $ctx, $layer, $extra]);
}
public function serializeMethod($method, $arguments, array $extra = [])
{
return $this->__call(__FUNCTION__, [$method, $arguments, $extra]);
}
public function serializeParams($tl, $arguments, $ctx, $layer = -1, array $extra = [])
{
return $this->__call(__FUNCTION__, [$tl, $arguments, $ctx, $layer, $extra]);
}
public function getLength($stream, $type = [
'type' => '',
], array $extra = [])
{
return $this->__call(__FUNCTION__, [$stream, $type, $extra]);
}
/**
* :type stream: io.BytesIO object.
*/
public function deserialize($stream, $type = [
'type' => '',
], array $extra = [])
{
return $this->__call(__FUNCTION__, [$stream, $type, $extra]);
}
public function htmlEntityDecode($stuff, array $extra = []) public function htmlEntityDecode($stuff, array $extra = [])
{ {
return $this->__call(__FUNCTION__, [$stuff, $extra]); return $this->__call(__FUNCTION__, [$stuff, $extra]);
@ -5442,6 +5442,19 @@ class InternalDoc extends APIFactory
{ {
return $this->__call(__FUNCTION__, [$extra]); return $this->__call(__FUNCTION__, [$extra]);
} }
/**
* Accesses a private variable from an object.
*
* @param object $obj Object
* @param string $var Attribute name
*
* @return mixed
* @access public
*/
public function getVar($obj, string $var, array $extra = [])
{
return $this->__call(__FUNCTION__, [$obj, $var, $extra]);
}
public function requestCall($user, array $extra = []) public function requestCall($user, array $extra = [])
{ {
@ -5483,28 +5496,58 @@ class InternalDoc extends APIFactory
{ {
return $this->__call(__FUNCTION__, [$extra]); return $this->__call(__FUNCTION__, [$extra]);
} }
/**
* Get dialog peers.
*
* @param boolean $force Whether to refetch all dialogs ignoring cache
*
* @return \Generator<array<Peer>>
*/
public function getDialogs($force = true, array $extra = []) public function getDialogs($force = true, array $extra = [])
{ {
return $this->__call(__FUNCTION__, [$force, $extra]); return $this->__call(__FUNCTION__, [$force, $extra]);
} }
/**
* Get full info of all dialogs.
*
* @param boolean $force Whether to refetch all dialogs ignoring cache
*
* @return \Generator
*/
public function getFullDialogs($force = true, array $extra = []) public function getFullDialogs($force = true, array $extra = [])
{ {
return $this->__call(__FUNCTION__, [$force, $extra]); return $this->__call(__FUNCTION__, [$force, $extra]);
} }
/**
* Set event handler.
*
* @param string|EventHandler $event_handler Event handler
*
* @return void
*/
public function setEventHandler($event_handler, array $extra = []) public function setEventHandler($event_handler, array $extra = [])
{ {
return $this->__call(__FUNCTION__, [$event_handler, $extra]); return $this->__call(__FUNCTION__, [$event_handler, $extra]);
} }
/**
public function getEventHandler(array $extra = []) * Get event handler.
*
* @return EventHandler
*/
public function getEventHandler(array $extra = []): EventHandler
{ {
return $this->__call(__FUNCTION__, [$extra]); return $this->__call(__FUNCTION__, [$extra]);
} }
/**
public function eventUpdateHandler($update, array $extra = []) * Event update handler.
*
* @param array $update Update
*
* @return void
*
* @internal Internal event handler
*/
public function eventUpdateHandler(array $update, array $extra = [])
{ {
return $this->__call(__FUNCTION__, [$update, $extra]); return $this->__call(__FUNCTION__, [$update, $extra]);
} }
@ -5645,17 +5688,31 @@ class InternalDoc extends APIFactory
{ {
return $this->__call(__FUNCTION__, [$template, $extra]); return $this->__call(__FUNCTION__, [$template, $extra]);
} }
/**
* Check for terms of service update.
*
* @return \Generator
*/
public function checkTos(array $extra = []) public function checkTos(array $extra = [])
{ {
return $this->__call(__FUNCTION__, [$extra]); return $this->__call(__FUNCTION__, [$extra]);
} }
/**
* Accept terms of service update.
*
* @return \Generator
*/
public function acceptTos(array $extra = []) public function acceptTos(array $extra = [])
{ {
return $this->__call(__FUNCTION__, [$extra]); return $this->__call(__FUNCTION__, [$extra]);
} }
/**
* Decline terms of service update.
*
* THIS WILL DELETE YOUR ACCOUNT!
*
* @return \Generator
*/
public function declineTos(array $extra = []) public function declineTos(array $extra = [])
{ {
return $this->__call(__FUNCTION__, [$extra]); return $this->__call(__FUNCTION__, [$extra]);

View File

@ -182,7 +182,7 @@ class WriteLoop extends ResumableSignalLoop
$temporary_keys = []; $temporary_keys = [];
if (\count($to_ack = $connection->ack_queue)) { if (\count($to_ack = $connection->ack_queue)) {
foreach (\array_chunk($connection->ack_queue, 8192) as $acks) { foreach (\array_chunk($connection->ack_queue, 8192) as $acks) {
$connection->pending_outgoing[$connection->pending_outgoing_key] = ['_' => 'msgs_ack', 'serialized_body' => yield $this->API->serializeObject(['type' => 'msgs_ack'], ['msg_ids' => $acks], 'msgs_ack'), 'contentRelated' => false, 'unencrypted' => false, 'method' => false]; $connection->pending_outgoing[$connection->pending_outgoing_key] = ['_' => 'msgs_ack', 'serialized_body' => yield $this->API->getTL()->serializeObject(['type' => 'msgs_ack'], ['msg_ids' => $acks], 'msgs_ack'), 'contentRelated' => false, 'unencrypted' => false, 'method' => false];
$temporary_keys[$connection->pending_outgoing_key] = true; $temporary_keys[$connection->pending_outgoing_key] = true;
$API->logger->logger("Adding msgs_ack {$connection->pending_outgoing_key}", Logger::ULTRA_VERBOSE); $API->logger->logger("Adding msgs_ack {$connection->pending_outgoing_key}", Logger::ULTRA_VERBOSE);
$connection->pending_outgoing_key++; $connection->pending_outgoing_key++;
@ -200,7 +200,7 @@ class WriteLoop extends ResumableSignalLoop
} }
if ($shared->isHttp() && !$has_http_wait) { if ($shared->isHttp() && !$has_http_wait) {
$API->logger->logger("Adding http_wait {$connection->pending_outgoing_key}", Logger::ULTRA_VERBOSE); $API->logger->logger("Adding http_wait {$connection->pending_outgoing_key}", Logger::ULTRA_VERBOSE);
$connection->pending_outgoing[$connection->pending_outgoing_key] = ['_' => 'http_wait', 'serialized_body' => yield $this->API->serializeObject(['type' => ''], ['_' => 'http_wait', 'max_wait' => 30000, 'wait_after' => 0, 'max_delay' => 0], 'http_wait'), 'contentRelated' => true, 'unencrypted' => false, 'method' => true]; $connection->pending_outgoing[$connection->pending_outgoing_key] = ['_' => 'http_wait', 'serialized_body' => yield $this->API->getTL()->serializeObject(['type' => ''], ['_' => 'http_wait', 'max_wait' => 30000, 'wait_after' => 0, 'max_delay' => 0], 'http_wait'), 'contentRelated' => true, 'unencrypted' => false, 'method' => true];
$temporary_keys[$connection->pending_outgoing_key] = true; $temporary_keys[$connection->pending_outgoing_key] = true;
$connection->pending_outgoing_key++; $connection->pending_outgoing_key++;
} }
@ -240,11 +240,11 @@ class WriteLoop extends ResumableSignalLoop
if (!$shared->getTempAuthKey()->isInited() && $message['_'] !== 'auth.bindTempAuthKey' && !$inited) { if (!$shared->getTempAuthKey()->isInited() && $message['_'] !== 'auth.bindTempAuthKey' && !$inited) {
$inited = true; $inited = true;
$API->logger->logger(\sprintf(\danog\MadelineProto\Lang::$current_lang['write_client_info'], $message['_']), \danog\MadelineProto\Logger::NOTICE); $API->logger->logger(\sprintf(\danog\MadelineProto\Lang::$current_lang['write_client_info'], $message['_']), \danog\MadelineProto\Logger::NOTICE);
$MTmessage['body'] = yield $API->serializeMethod( $MTmessage['body'] = yield $API->getTL()->serializeMethod(
'invokeWithLayer', 'invokeWithLayer',
[ [
'layer' => $API->settings['tl_schema']['layer'], 'layer' => $API->settings['tl_schema']['layer'],
'query' => yield $API->serializeMethod( 'query' => yield $API->getTL()->serializeMethod(
'initConnection', 'initConnection',
[ [
'api_id' => $API->settings['app_info']['api_id'], 'api_id' => $API->settings['app_info']['api_id'],
@ -266,7 +266,7 @@ class WriteLoop extends ResumableSignalLoop
if (!isset($connection->call_queue[$message['queue']])) { if (!isset($connection->call_queue[$message['queue']])) {
$connection->call_queue[$message['queue']] = []; $connection->call_queue[$message['queue']] = [];
} }
$MTmessage['body'] = yield $API->serializeMethod('invokeAfterMsgs', ['msg_ids' => $connection->call_queue[$message['queue']], 'query' => $MTmessage['body']]); $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; $connection->call_queue[$message['queue']][$message_id] = $message_id;
if (\count($connection->call_queue[$message['queue']]) > $API->settings['msg_array_limit']['call_queue']) { if (\count($connection->call_queue[$message['queue']]) > $API->settings['msg_array_limit']['call_queue']) {
@ -278,7 +278,7 @@ class WriteLoop extends ResumableSignalLoop
// TODO // TODO
/* if ($API->settings['requests']['gzip_encode_if_gt'] !== -1 && ($l = strlen($MTmessage['body'])) > $API->settings['requests']['gzip_encode_if_gt']) { /* if ($API->settings['requests']['gzip_encode_if_gt'] !== -1 && ($l = strlen($MTmessage['body'])) > $API->settings['requests']['gzip_encode_if_gt']) {
if (($g = strlen($gzipped = gzencode($MTmessage['body']))) < $l) { if (($g = strlen($gzipped = gzencode($MTmessage['body']))) < $l) {
$MTmessage['body'] = yield $API->serializeObject(['type' => 'gzip_packed'], ['packed_data' => $gzipped], 'gzipped data'); $MTmessage['body'] = yield $API->getTL()->serializeObject(['type' => 'gzip_packed'], ['packed_data' => $gzipped], 'gzipped data');
$API->logger->logger('Using GZIP compression for ' . $message['_'] . ', saved ' . ($l - $g) . ' bytes of data, reduced call size by ' . $g * 100 / $l . '%', \danog\MadelineProto\Logger::ULTRA_VERBOSE); $API->logger->logger('Using GZIP compression for ' . $message['_'] . ', saved ' . ($l - $g) . ' bytes of data, reduced call size by ' . $g * 100 / $l . '%', \danog\MadelineProto\Logger::ULTRA_VERBOSE);
} }
unset($gzipped); unset($gzipped);
@ -317,7 +317,7 @@ class WriteLoop extends ResumableSignalLoop
//var_dumP("container ".bin2hex($message_id)); //var_dumP("container ".bin2hex($message_id));
$keys[$connection->pending_outgoing_key++] = $message_id; $keys[$connection->pending_outgoing_key++] = $message_id;
$message_data = yield $API->serializeObject(['type' => ''], ['_' => 'msg_container', 'messages' => $messages], 'container'); $message_data = yield $API->getTL()->serializeObject(['type' => ''], ['_' => 'msg_container', 'messages' => $messages], 'container');
$message_data_length = \strlen($message_data); $message_data_length = \strlen($message_data);
$seq_no = $connection->generateOutSeqNo(false); $seq_no = $connection->generateOutSeqNo(false);

View File

@ -31,6 +31,7 @@ use danog\MadelineProto\MTProtoTools\CombinedUpdatesState;
use danog\MadelineProto\MTProtoTools\MinDatabase; use danog\MadelineProto\MTProtoTools\MinDatabase;
use danog\MadelineProto\MTProtoTools\ReferenceDatabase; use danog\MadelineProto\MTProtoTools\ReferenceDatabase;
use danog\MadelineProto\MTProtoTools\UpdatesState; use danog\MadelineProto\MTProtoTools\UpdatesState;
use danog\MadelineProto\TL\TL;
use danog\MadelineProto\TL\TLCallback; use danog\MadelineProto\TL\TLCallback;
/** /**
@ -49,7 +50,6 @@ class MTProto extends AsyncConstruct implements TLCallback
use \danog\MadelineProto\SecretChats\MessageHandler; use \danog\MadelineProto\SecretChats\MessageHandler;
use \danog\MadelineProto\SecretChats\ResponseHandler; use \danog\MadelineProto\SecretChats\ResponseHandler;
use \danog\MadelineProto\SecretChats\SeqNoHandler; use \danog\MadelineProto\SecretChats\SeqNoHandler;
use \danog\MadelineProto\TL\TL;
use \danog\MadelineProto\TL\Conversion\BotAPI; use \danog\MadelineProto\TL\Conversion\BotAPI;
use \danog\MadelineProto\TL\Conversion\BotAPIFiles; use \danog\MadelineProto\TL\Conversion\BotAPIFiles;
use \danog\MadelineProto\TL\Conversion\Extension; use \danog\MadelineProto\TL\Conversion\Extension;
@ -163,10 +163,24 @@ class MTProto extends AsyncConstruct implements TLCallback
64 => ' and content-related response to message already generated', 64 => ' and content-related response to message already generated',
128 => ' and other party knows for a fact that message is already received' 128 => ' and other party knows for a fact that message is already received'
]; ];
const REQUESTED = 0; /**
const ACCEPTED = 1; * Secret chat was not found.
const CONFIRMED = 2; *
const READY = 3; * @var int
*/
const SECRET_EMPTY = 0;
/**
* Secret chat was requested.
*
* @var int
*/
const SECRET_REQUESTED = 1;
/**
* Secret chat was found.
*
* @var int
*/
const SECRET_READY = 2;
const TD_PARAMS_CONVERSION = ['updateNewMessage' => ['_' => 'updateNewMessage', 'disable_notification' => ['message', 'silent'], 'message' => ['message']], 'message' => ['_' => 'message', 'id' => ['id'], 'sender_user_id' => ['from_id'], 'chat_id' => ['to_id', 'choose_chat_id_from_botapi'], 'send_state' => ['choose_incoming_or_sent'], 'can_be_edited' => ['choose_can_edit'], 'can_be_deleted' => ['choose_can_delete'], 'is_post' => ['post'], 'date' => ['date'], 'edit_date' => ['edit_date'], 'forward_info' => ['fwd_info', 'choose_forward_info'], 'reply_to_message_id' => ['reply_to_msg_id'], 'ttl' => ['choose_ttl'], 'ttl_expires_in' => ['choose_ttl_expires_in'], 'via_bot_user_id' => ['via_bot_id'], 'views' => ['views'], 'content' => ['choose_message_content'], 'reply_markup' => ['reply_markup']], 'messages.sendMessage' => ['chat_id' => ['peer'], 'reply_to_message_id' => ['reply_to_msg_id'], 'disable_notification' => ['silent'], 'from_background' => ['background'], 'input_message_content' => ['choose_message_content'], 'reply_markup' => ['reply_markup']]]; const TD_PARAMS_CONVERSION = ['updateNewMessage' => ['_' => 'updateNewMessage', 'disable_notification' => ['message', 'silent'], 'message' => ['message']], 'message' => ['_' => 'message', 'id' => ['id'], 'sender_user_id' => ['from_id'], 'chat_id' => ['to_id', 'choose_chat_id_from_botapi'], 'send_state' => ['choose_incoming_or_sent'], 'can_be_edited' => ['choose_can_edit'], 'can_be_deleted' => ['choose_can_delete'], 'is_post' => ['post'], 'date' => ['date'], 'edit_date' => ['edit_date'], 'forward_info' => ['fwd_info', 'choose_forward_info'], 'reply_to_message_id' => ['reply_to_msg_id'], 'ttl' => ['choose_ttl'], 'ttl_expires_in' => ['choose_ttl_expires_in'], 'via_bot_user_id' => ['via_bot_id'], 'views' => ['views'], 'content' => ['choose_message_content'], 'reply_markup' => ['reply_markup']], 'messages.sendMessage' => ['chat_id' => ['peer'], 'reply_to_message_id' => ['reply_to_msg_id'], 'disable_notification' => ['silent'], 'from_background' => ['background'], 'input_message_content' => ['choose_message_content'], 'reply_markup' => ['reply_markup']]];
const TD_REVERSE = ['sendMessage' => 'messages.sendMessage']; const TD_REVERSE = ['sendMessage' => 'messages.sendMessage'];
const TD_IGNORE = ['updateMessageID']; const TD_IGNORE = ['updateMessageID'];
@ -419,6 +433,13 @@ class MTProto extends AsyncConstruct implements TLCallback
*/ */
public $logger; public $logger;
/**
* TL serializer.
*
* @var \danog\MadelineProto\TL\TL
*/
private $TL;
/** /**
* Constructor function. * Constructor function.
* *
@ -449,19 +470,17 @@ class MTProto extends AsyncConstruct implements TLCallback
$this->logger->logger(Lang::$current_lang['load_rsa'], Logger::ULTRA_VERBOSE); $this->logger->logger(Lang::$current_lang['load_rsa'], Logger::ULTRA_VERBOSE);
$this->rsa_keys = []; $this->rsa_keys = [];
foreach ($this->settings['authorization']['rsa_keys'] as $key) { foreach ($this->settings['authorization']['rsa_keys'] as $key) {
$key = yield (new RSA())->load($key); $key = yield (new RSA())->load($this->TL, $key);
$this->rsa_keys[$key->fp] = $key; $this->rsa_keys[$key->fp] = $key;
} }
/* // (re)-initialize TL
* ***********************************************************************
* Define some needed numbers for BigInteger
*/
$this->logger->logger(Lang::$current_lang['TL_translation'], Logger::ULTRA_VERBOSE); $this->logger->logger(Lang::$current_lang['TL_translation'], Logger::ULTRA_VERBOSE);
$callbacks = [$this, $this->referenceDatabase]; $callbacks = [$this, $this->referenceDatabase];
if (!($this->authorization['user']['bot'] ?? false)) { if (!($this->authorization['user']['bot'] ?? false)) {
$callbacks []= $this->minDatabase; $callbacks []= $this->minDatabase;
} }
$this->constructTL($this->settings['tl_schema']['src'], $callbacks); $this->TL->init($this->settings['tl_schema']['src'], $callbacks);
yield $this->connectToAllDcs(); yield $this->connectToAllDcs();
$this->startLoops(); $this->startLoops();
$this->datacenter->curdc = 2; $this->datacenter->curdc = 2;
@ -544,17 +563,11 @@ class MTProto extends AsyncConstruct implements TLCallback
// Version // Version
'v', 'v',
// TL (we don't need this) // TL
'constructors', 'TL',
'td_constructors',
'methods',
'td_methods',
'td_descriptions',
'tl_callbacks',
// Secret chats // Secret chats
'secret_chats', 'secret_chats',
'encrypted_layer',
'temp_requested_secret_chats', 'temp_requested_secret_chats',
'temp_rekeyed_secret_chats', 'temp_rekeyed_secret_chats',
@ -576,7 +589,7 @@ class MTProto extends AsyncConstruct implements TLCallback
if (!($this->authorization['user']['bot'] ?? false)) { if (!($this->authorization['user']['bot'] ?? false)) {
$callbacks []= $this->minDatabase; $callbacks []= $this->minDatabase;
} }
$this->updateCallbacks($callbacks); $this->TL->updateCallbacks($callbacks);
return $this; return $this;
} }
@ -598,6 +611,37 @@ class MTProto extends AsyncConstruct implements TLCallback
return isset($this->logger) ? $this->logger->logger($param, $level, $file) : Logger::$default->logger($param, $level, $file); return isset($this->logger) ? $this->logger->logger($param, $level, $file) : Logger::$default->logger($param, $level, $file);
} }
/**
* Get TL namespaces.
*
* @return array
*/
public function getMethodNamespaces(): array
{
return $this->TL->getMethodNamespaces();
}
/**
* Get namespaced methods (method => namespace).
*
* @return array
*/
public function getMethodsNamespaced(): array
{
return $this->TL->getMethodsNamespaced();
}
/**
* Get TL serializer.
*
* @return TL
*/
public function getTL(): TL
{
return $this->TL;
}
/** /**
* Get async HTTP client. * Get async HTTP client.
* *
@ -736,9 +780,18 @@ class MTProto extends AsyncConstruct implements TLCallback
if (!isset($this->minDatabase)) { if (!isset($this->minDatabase)) {
$this->minDatabase = new MinDatabase($this); $this->minDatabase = new MinDatabase($this);
} }
if (!isset($this->TL)) {
$this->TL = new TL($this);
$this->logger->logger(Lang::$current_lang['TL_translation'], Logger::ULTRA_VERBOSE);
$callbacks = [$this, $this->referenceDatabase];
if (!($this->authorization['user']['bot'] ?? false)) {
$callbacks []= $this->minDatabase;
}
$this->TL->init($this->settings['tl_schema']['src'], $callbacks);
}
} }
/** /**
* Upgrade MadelineProto instance * Upgrade MadelineProto instance.
* *
* @return \Generator * @return \Generator
*/ */
@ -863,7 +916,7 @@ class MTProto extends AsyncConstruct implements TLCallback
if (!($this->authorization['user']['bot'] ?? false)) { if (!($this->authorization['user']['bot'] ?? false)) {
$callbacks []= $this->minDatabase; $callbacks []= $this->minDatabase;
} }
$this->updateCallbacks($callbacks); $this->TL->updateCallbacks($callbacks);
$this->settings['connection_settings']['all']['ipv6'] = Magic::$ipv6; $this->settings['connection_settings']['all']['ipv6'] = Magic::$ipv6;
if ($this->authorized === true) { if ($this->authorized === true) {
@ -1350,6 +1403,11 @@ class MTProto extends AsyncConstruct implements TLCallback
return $this->datacenter->isHttp($datacenter); return $this->datacenter->isHttp($datacenter);
} }
/**
* Checks whether all datacenters are authorized.
*
* @return boolean
*/
public function hasAllAuth(): bool public function hasAllAuth(): bool
{ {
if ($this->isInitingAuthorization()) { if ($this->isInitingAuthorization()) {
@ -1365,6 +1423,11 @@ class MTProto extends AsyncConstruct implements TLCallback
return true; return true;
} }
/**
* Whether we're initing authorization.
*
* @return boolean
*/
public function isInitingAuthorization() public function isInitingAuthorization()
{ {
return $this->initing_authorization; return $this->initing_authorization;
@ -1563,8 +1626,7 @@ class MTProto extends AsyncConstruct implements TLCallback
{ {
try { try {
foreach ((yield $this->methodCallAsyncRead('help.getCdnConfig', [], ['datacenter' => $datacenter]))['public_keys'] as $curkey) { foreach ((yield $this->methodCallAsyncRead('help.getCdnConfig', [], ['datacenter' => $datacenter]))['public_keys'] as $curkey) {
$tempkey = new \danog\MadelineProto\RSA($curkey['public_key']); $this->cdn_rsa_keys[$tempkey->fp] = yield (new RSA)->load($this->TL, $curkey['public_key']);
$this->cdn_rsa_keys[$tempkey->fp] = $tempkey;
} }
} catch (\danog\MadelineProto\TL\Exception $e) { } catch (\danog\MadelineProto\TL\Exception $e) {
$this->logger->logger($e->getMessage(), \danog\MadelineProto\Logger::FATAL_ERROR); $this->logger->logger($e->getMessage(), \danog\MadelineProto\Logger::FATAL_ERROR);

View File

@ -186,7 +186,7 @@ trait AuthKeyHandler
$q_bytes = $q->toBytes(); $q_bytes = $q->toBytes();
$new_nonce = \danog\MadelineProto\Tools::random(32); $new_nonce = \danog\MadelineProto\Tools::random(32);
$data_unserialized = ['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)]; $data_unserialized = ['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->serializeObject(['type' => 'p_q_inner_data'.($expires_in < 0 ? '' : '_temp')], $data_unserialized, 'p_q_inner_data'); $p_q_inner_data = yield $this->TL->serializeObject(['type' => 'p_q_inner_data'.($expires_in < 0 ? '' : '_temp')], $data_unserialized, 'p_q_inner_data');
/* /*
* *********************************************************************** * ***********************************************************************
* Encrypt serialized object * Encrypt serialized object
@ -265,12 +265,12 @@ trait AuthKeyHandler
* int $server_time * int $server_time
* ] * ]
*/ */
$server_DH_inner_data = $this->deserialize($answer, ['type' => '']); $server_DH_inner_data = $this->TL->deserialize($answer, ['type' => '']);
/* /*
* *********************************************************************** * ***********************************************************************
* Do some checks * Do some checks
*/ */
$server_DH_inner_data_length = $this->getLength($answer); $server_DH_inner_data_length = $this->TL->getLength($answer);
if (\sha1(\substr($answer, 0, $server_DH_inner_data_length), true) != $answer_hash) { if (\sha1(\substr($answer, 0, $server_DH_inner_data_length), true) != $answer_hash) {
throw new \danog\MadelineProto\SecurityException('answer_hash mismatch.'); throw new \danog\MadelineProto\SecurityException('answer_hash mismatch.');
} }
@ -320,7 +320,7 @@ trait AuthKeyHandler
* string $g_b : g^b mod dh_prime * string $g_b : g^b mod dh_prime
* ] * ]
*/ */
$data = yield $this->serializeObject(['type' => 'client_DH_inner_data'], ['nonce' => $nonce, 'server_nonce' => $server_nonce, 'retry_id' => $retry_id, 'g_b' => $g_b_str], 'client_DH_inner_data'); $data = yield $this->TL->serializeObject(['type' => 'client_DH_inner_data'], ['nonce' => $nonce, 'server_nonce' => $server_nonce, 'retry_id' => $retry_id, 'g_b' => $g_b_str], 'client_DH_inner_data');
/* /*
* *********************************************************************** * ***********************************************************************
* encrypt client_DH_inner_data * encrypt client_DH_inner_data
@ -550,7 +550,7 @@ trait AuthKeyHandler
$temp_auth_key_id = $datacenterConnection->getTempAuthKey()->getID(); $temp_auth_key_id = $datacenterConnection->getTempAuthKey()->getID();
$perm_auth_key_id = $datacenterConnection->getPermAuthKey()->getID(); $perm_auth_key_id = $datacenterConnection->getPermAuthKey()->getID();
$temp_session_id = $connection->session_id; $temp_session_id = $connection->session_id;
$message_data = yield $this->serializeObject(['type' => 'bind_auth_key_inner'], ['nonce' => $nonce, 'temp_auth_key_id' => $temp_auth_key_id, 'perm_auth_key_id' => $perm_auth_key_id, 'temp_session_id' => $temp_session_id, 'expires_at' => $expires_at], 'bindTempAuthKey_inner'); $message_data = yield $this->TL->serializeObject(['type' => 'bind_auth_key_inner'], ['nonce' => $nonce, 'temp_auth_key_id' => $temp_auth_key_id, 'perm_auth_key_id' => $perm_auth_key_id, 'temp_session_id' => $temp_session_id, 'expires_at' => $expires_at], 'bindTempAuthKey_inner');
$message_id = $connection->generateMessageId(); $message_id = $connection->generateMessageId();
$seq_no = 0; $seq_no = 0;
$encrypted_data = \danog\MadelineProto\Tools::random(16).$message_id.\pack('VV', $seq_no, \strlen($message_data)).$message_data; $encrypted_data = \danog\MadelineProto\Tools::random(16).$message_id.\pack('VV', $seq_no, \strlen($message_data)).$message_data;

View File

@ -18,41 +18,43 @@
namespace danog\MadelineProto; namespace danog\MadelineProto;
use danog\MadelineProto\TL\TL;
/** /**
* RSA class * RSA class.
*/ */
class RSA class RSA
{ {
use \danog\MadelineProto\TL\TL;
use \danog\MadelineProto\Tools; use \danog\MadelineProto\Tools;
use \danog\Serializable; use \danog\Serializable;
/** /**
* Exponent * Exponent.
* *
* @var \phpseclib\Math\BigInteger * @var \phpseclib\Math\BigInteger
*/ */
public $e; public $e;
/** /**
* Modulus * Modulus.
* *
* @var \phpseclib\Math\BigInteger * @var \phpseclib\Math\BigInteger
*/ */
public $n; public $n;
/** /**
* Fingerprint * Fingerprint.
* *
* @var string * @var string
*/ */
public $fp; public $fp;
/** /**
* Load RSA key * Load RSA key.
* *
* @param TL $TL TL serializer
* @param string $rsa_key RSA key * @param string $rsa_key RSA key
* *
* @return \Generator<self> * @return \Generator<self>
*/ */
public function load(string $rsa_key): \Generator public function load(TL $TL, string $rsa_key): \Generator
{ {
\danog\MadelineProto\Logger::log(\danog\MadelineProto\Lang::$current_lang['rsa_init'], Logger::ULTRA_VERBOSE); \danog\MadelineProto\Logger::log(\danog\MadelineProto\Lang::$current_lang['rsa_init'], Logger::ULTRA_VERBOSE);
\danog\MadelineProto\Logger::log(\danog\MadelineProto\Lang::$current_lang['loading_key'], Logger::ULTRA_VERBOSE); \danog\MadelineProto\Logger::log(\danog\MadelineProto\Lang::$current_lang['loading_key'], Logger::ULTRA_VERBOSE);
@ -60,13 +62,13 @@ class RSA
$this->n = Tools::getVar($key, 'modulus'); $this->n = Tools::getVar($key, 'modulus');
$this->e = Tools::getVar($key, 'exponent'); $this->e = Tools::getVar($key, 'exponent');
\danog\MadelineProto\Logger::log(\danog\MadelineProto\Lang::$current_lang['computing_fingerprint'], Logger::ULTRA_VERBOSE); \danog\MadelineProto\Logger::log(\danog\MadelineProto\Lang::$current_lang['computing_fingerprint'], Logger::ULTRA_VERBOSE);
$this->fp = \substr(\sha1((yield $this->serializeObject(['type' => 'bytes'], $this->n->toBytes(), 'key')).(yield $this->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; return $this;
} }
/** /**
* Sleep function * Sleep function.
* *
* @return array * @return array
*/ */
@ -76,7 +78,7 @@ class RSA
} }
/** /**
* Encrypt data * Encrypt data.
* *
* @param string $data Data to encrypt * @param string $data Data to encrypt
* *

View File

@ -19,6 +19,8 @@
namespace danog\MadelineProto\SecretChats; namespace danog\MadelineProto\SecretChats;
use danog\MadelineProto\MTProto;
/** /**
* Manages secret chats. * Manages secret chats.
* *
@ -26,10 +28,27 @@ namespace danog\MadelineProto\SecretChats;
*/ */
trait AuthKeyHandler trait AuthKeyHandler
{ {
/**
* Temporary requested secret chats.
*
* @var array
*/
protected $temp_requested_secret_chats = []; protected $temp_requested_secret_chats = [];
/**
* Secret chats.
*
* @var array
*/
protected $secret_chats = []; protected $secret_chats = [];
public function acceptSecretChat($params) /**
* Accept secret chat.
*
* @param array $params Secret chat ID
*
* @return \Generator
*/
public function acceptSecretChat($params): \Generator
{ {
//$this->logger->logger($params['id'],$this->secretChatStatus($params['id'])); //$this->logger->logger($params['id'],$this->secretChatStatus($params['id']));
if ($this->secretChatStatus($params['id']) !== 0) { if ($this->secretChatStatus($params['id']) !== 0) {
@ -56,7 +75,14 @@ trait AuthKeyHandler
$this->logger->logger('Secret chat '.$params['id'].' accepted successfully!', \danog\MadelineProto\Logger::NOTICE); $this->logger->logger('Secret chat '.$params['id'].' accepted successfully!', \danog\MadelineProto\Logger::NOTICE);
} }
public function requestSecretChat($user) /**
* Request secret chat.
*
* @param mixed $user User to start secret chat with
*
* @return \Generator
*/
public function requestSecretChat($user): \Generator
{ {
$user = yield $this->getInfo($user); $user = yield $this->getInfo($user);
if (!isset($user['InputUser'])) { if (!isset($user['InputUser'])) {
@ -78,7 +104,14 @@ trait AuthKeyHandler
return $res['id']; return $res['id'];
} }
public function completeSecretChat($params) /**
* Complete secret chat.
*
* @param array $params Secret chat
*
* @return \Generator
*/
private function completeSecretChat($params): \Generator
{ {
if ($this->secretChatStatus($params['id']) !== 1) { if ($this->secretChatStatus($params['id']) !== 1) {
//$this->logger->logger($this->secretChatStatus($params['id'])); //$this->logger->logger($this->secretChatStatus($params['id']));
@ -105,14 +138,26 @@ trait AuthKeyHandler
$this->logger->logger('Secret chat '.$params['id'].' completed successfully!', \danog\MadelineProto\Logger::NOTICE); $this->logger->logger('Secret chat '.$params['id'].' completed successfully!', \danog\MadelineProto\Logger::NOTICE);
} }
public function notifyLayer($chat) private function notifyLayer($chat): \Generator
{ {
yield $this->methodCallAsyncRead('messages.sendEncryptedService', ['peer' => $chat, 'message' => ['_' => 'decryptedMessageService', 'action' => ['_' => 'decryptedMessageActionNotifyLayer', 'layer' => $this->encrypted_layer]]], ['datacenter' => $this->datacenter->curdc]); 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 = []; protected $temp_rekeyed_secret_chats = [];
public function rekey($chat) /**
* Rekey secret chat.
*
* @param mixed $chat Secret chat to rekey
*
* @return \Generator
*/
public function rekey($chat): \Generator
{ {
if ($this->secret_chats[$chat]['rekeying'][0] !== 0) { if ($this->secret_chats[$chat]['rekeying'][0] !== 0) {
return; return;
@ -133,7 +178,15 @@ trait AuthKeyHandler
return $e; return $e;
} }
public function acceptRekey($chat, $params) /**
* Accept rekeying.
*
* @param mixed $chat Chat
* @param array $params Parameters
*
* @return \Generator
*/
private function acceptRekey($chat, array $params): \Generator
{ {
if ($this->secret_chats[$chat]['rekeying'][0] !== 0) { if ($this->secret_chats[$chat]['rekeying'][0] !== 0) {
$my_exchange_id = new \phpseclib\Math\BigInteger($this->secret_chats[$chat]['rekeying'][1], -256); $my_exchange_id = new \phpseclib\Math\BigInteger($this->secret_chats[$chat]['rekeying'][1], -256);
@ -166,7 +219,15 @@ trait AuthKeyHandler
$this->updaters[false]->resume(); $this->updaters[false]->resume();
} }
public function commitRekey($chat, $params) /**
* Commit rekeying of secret chat.
*
* @param mixed $chat Chat
* @param array $params Parameters
*
* @return \Generator
*/
private function commitRekey($chat, array $params): \Generator
{ {
if ($this->secret_chats[$chat]['rekeying'][0] !== 1 || !isset($this->temp_rekeyed_secret_chats[$params['exchange_id']])) { if ($this->secret_chats[$chat]['rekeying'][0] !== 1 || !isset($this->temp_rekeyed_secret_chats[$params['exchange_id']])) {
$this->secret_chats[$chat]['rekeying'] = [0]; $this->secret_chats[$chat]['rekeying'] = [0];
@ -196,7 +257,15 @@ trait AuthKeyHandler
$this->updaters[false]->resume(); $this->updaters[false]->resume();
} }
public function completeRekey($chat, $params) /**
* Complete rekeying.
*
* @param mixed $chat Chat
* @param array $params Parameters
*
* @return \Generator
*/
private function completeRekey($chat, array $params): \Generator
{ {
if ($this->secret_chats[$chat]['rekeying'][0] !== 2 || !isset($this->temp_rekeyed_secret_chats['fingerprint'])) { if ($this->secret_chats[$chat]['rekeying'][0] !== 2 || !isset($this->temp_rekeyed_secret_chats['fingerprint'])) {
return; return;
@ -219,27 +288,59 @@ trait AuthKeyHandler
return true; return true;
} }
public function secretChatStatus($chat) /**
* Get secret chat status.
*
* @param int $chat Chat ID
*
* @return int One of MTProto::SECRET_EMPTY, MTProto::SECRET_REQUESTED, MTProto::SECRET_READY
*/
public function secretChatStatus(int $chat): int
{ {
if (isset($this->secret_chats[$chat])) { if (isset($this->secret_chats[$chat])) {
return 2; return MTProto::SECRET_READY;
} }
if (isset($this->temp_requested_secret_chats[$chat])) { if (isset($this->temp_requested_secret_chats[$chat])) {
return 1; return MTProto::SECRET_REQUESTED;
} }
return 0; return MTProto::SECRET_EMPTY;
} }
public function getSecretChat($chat) /**
* Get secret chat.
*
* @param array|int $chat Secret chat ID
*
* @return array
*/
public function getSecretChat($chat): array
{ {
return $this->secret_chats[\is_array($chat) ? $chat['chat_id'] : $chat]; return $this->secret_chats[\is_array($chat) ? $chat['chat_id'] : $chat];
} }
public function discardSecretChat($chat) /**
* Check whether secret chat exists.
*
* @param array|int $chat Secret chat ID
*
* @return boolean
*/
public function hasSecretChat($chat): bool
{
return isset($this->secret_chats[\is_array($chat) ? $chat['chat_id'] : $chat]);
}
/**
* Discard secret chat.
*
* @param array|int $chat Secret chat ID
*
* @return \Generator
*/
public function discardSecretChat($chat): \Generator
{ {
$this->logger->logger('Discarding secret chat '.$chat.'...', \danog\MadelineProto\Logger::VERBOSE); $this->logger->logger('Discarding secret chat '.$chat.'...', \danog\MadelineProto\Logger::VERBOSE);
//$this->logger->logger(debug_backtrace(0)[0]);
if (isset($this->secret_chats[$chat])) { if (isset($this->secret_chats[$chat])) {
unset($this->secret_chats[$chat]); unset($this->secret_chats[$chat]);
} }

View File

@ -41,7 +41,7 @@ trait MessageHandler
$this->secret_chats[$chat_id]['out_seq_no']++; $this->secret_chats[$chat_id]['out_seq_no']++;
} }
$this->secret_chats[$chat_id]['outgoing'][$this->secret_chats[$chat_id]['out_seq_no']] = $message; $this->secret_chats[$chat_id]['outgoing'][$this->secret_chats[$chat_id]['out_seq_no']] = $message;
$message = yield $this->serializeObject(['type' => $constructor = $this->secret_chats[$chat_id]['layer'] === 8 ? 'DecryptedMessage' : 'DecryptedMessageLayer'], $message, $constructor, $this->secret_chats[$chat_id]['layer']); $message = yield $this->TL->serializeObject(['type' => $constructor = $this->secret_chats[$chat_id]['layer'] === 8 ? 'DecryptedMessage' : 'DecryptedMessageLayer'], $message, $constructor, $this->secret_chats[$chat_id]['layer']);
$message = \danog\MadelineProto\Tools::packUnsignedInt(\strlen($message)).$message; $message = \danog\MadelineProto\Tools::packUnsignedInt(\strlen($message)).$message;
if ($this->secret_chats[$chat_id]['mtproto'] === 2) { if ($this->secret_chats[$chat_id]['mtproto'] === 2) {
$padding = \danog\MadelineProto\Tools::posmod(-\strlen($message), 16); $padding = \danog\MadelineProto\Tools::posmod(-\strlen($message), 16);
@ -111,7 +111,7 @@ trait MessageHandler
$this->secret_chats[$message['message']['chat_id']]['mtproto'] = 2; $this->secret_chats[$message['message']['chat_id']]['mtproto'] = 2;
} }
} }
$deserialized = $this->deserialize($message_data, ['type' => '']); $deserialized = $this->TL->deserialize($message_data, ['type' => '']);
$this->secret_chats[$message['message']['chat_id']]['ttr']--; $this->secret_chats[$message['message']['chat_id']]['ttr']--;
if (($this->secret_chats[$message['message']['chat_id']]['ttr'] <= 0 || \time() - $this->secret_chats[$message['message']['chat_id']]['updated'] > 7 * 24 * 60 * 60) && $this->secret_chats[$message['message']['chat_id']]['rekeying'][0] === 0) { if (($this->secret_chats[$message['message']['chat_id']]['ttr'] <= 0 || \time() - $this->secret_chats[$message['message']['chat_id']]['updated'] > 7 * 24 * 60 * 60) && $this->secret_chats[$message['message']['chat_id']]['rekeying'][0] === 0) {
yield $this->rekey($message['message']['chat_id']); yield $this->rekey($message['message']['chat_id']);

View File

@ -374,7 +374,7 @@ trait BotAPI
$data['document']['_'] = 'bot_'.$type_name; $data['document']['_'] = 'bot_'.$type_name;
$res['file_size'] = $data['document']['size']; $res['file_size'] = $data['document']['size'];
$res['mime_type'] = $data['document']['mime_type']; $res['mime_type'] = $data['document']['mime_type'];
$res['file_id'] = \danog\MadelineProto\Tools::base64urlEncode(\danog\MadelineProto\Tools::rleEncode((yield $this->serializeObject(['type' => 'File'], $data['document'], 'File')).\chr(2))); $res['file_id'] = \danog\MadelineProto\Tools::base64urlEncode(\danog\MadelineProto\Tools::rleEncode((yield $this->TL->serializeObject(['type' => 'File'], $data['document'], 'File')).\chr(2)));
return [$type_name => $res, 'caption' => isset($data['caption']) ? $data['caption'] : '']; return [$type_name => $res, 'caption' => isset($data['caption']) ? $data['caption'] : ''];
default: default:

View File

@ -29,7 +29,7 @@ trait BotAPIFiles
$photoSize['location']['secret'] = $photo['location']['secret'] ?? 0; $photoSize['location']['secret'] = $photo['location']['secret'] ?? 0;
$photoSize['location']['dc_id'] = $photo['dc_id'] ?? 0; $photoSize['location']['dc_id'] = $photo['dc_id'] ?? 0;
$photoSize['location']['_'] = $thumbnail ? 'bot_thumbnail' : 'bot_photo'; $photoSize['location']['_'] = $thumbnail ? 'bot_thumbnail' : 'bot_photo';
$data = (yield $this->serializeObject(['type' => 'File'], $photoSize['location'], 'File')).\chr(2); $data = (yield $this->TL->serializeObject(['type' => 'File'], $photoSize['location'], 'File')).\chr(2);
return [ return [
'file_id' => \danog\MadelineProto\Tools::base64urlEncode(\danog\MadelineProto\Tools::rleEncode($data)), 'file_id' => \danog\MadelineProto\Tools::base64urlEncode(\danog\MadelineProto\Tools::rleEncode($data)),
@ -47,7 +47,7 @@ trait BotAPIFiles
if ($file_id[\strlen($file_id) - 1] !== \chr(2)) { if ($file_id[\strlen($file_id) - 1] !== \chr(2)) {
throw new Exception(\danog\MadelineProto\Lang::$current_lang['last_byte_invalid']); throw new Exception(\danog\MadelineProto\Lang::$current_lang['last_byte_invalid']);
} }
$deserialized = $this->deserialize($file_id); $deserialized = $this->TL->deserialize($file_id);
$res = ['type' => \str_replace('bot_', '', $deserialized['_'])]; $res = ['type' => \str_replace('bot_', '', $deserialized['_'])];
switch ($deserialized['_']) { switch ($deserialized['_']) {
case 'bot_thumbnail': case 'bot_thumbnail':

View File

@ -22,69 +22,123 @@ namespace danog\MadelineProto\TL;
use danog\MadelineProto\MTProto; use danog\MadelineProto\MTProto;
/** /**
* TL serialization * TL serialization.
*/ */
class TL class TL
{ {
/** /**
* Highest available secret chat layer version * Highest available secret chat layer version.
* *
* @var integer * @var integer
*/ */
public $encrypted_layer = -1; private $encrypted_layer = -1;
/** /**
* Constructors * Constructors.
* *
* @var TLConstructor * @var TLConstructor
*/ */
public $constructors; private $constructors;
/** /**
* Methods * Methods.
* *
* @var TLMethods * @var TLMethod
*/ */
public $methods; private $methods;
/** /**
* TD Constructors * TD Constructors.
* *
* @var TLConstructors * @var TLConstructors
*/ */
public $td_constructors; private $td_constructors;
/** /**
* TD Methods * TD Methods.
* *
* @var TLMethods * @var TLMethod
*/ */
public $td_methods; private $td_methods;
/** /**
* Descriptions * Descriptions.
* *
* @var array * @var array
*/ */
public $td_descriptions; private $td_descriptions;
/** /**
* TL callbacks * TL callbacks.
* *
* @var array * @var array
*/ */
public $tl_callbacks = []; private $tl_callbacks = [];
/** /**
* API instance * API instance.
* *
* @var \danog\MadelineProto\MTProto * @var \danog\MadelineProto\MTProto
*/ */
private $API; private $API;
/** /**
* Constructor function * Constructor function.
* *
* @param MTProto $API API instance * @param MTProto $API API instance
*/ */
public function __construct(MTProto $API) { public function __construct($API = null)
{
$this->API = $API; $this->API = $API;
} }
public function constructTL($files, $objects = [])
/**
* Get secret chat layer version.
*
* @return integer
*/
public function getSecretLayer(): int
{ {
$this->logger->logger(\danog\MadelineProto\Lang::$current_lang['TL_loading'], \danog\MadelineProto\Logger::VERBOSE); return $this->encrypted_layer;
}
/**
* Get constructors.
*
* @param int $td Whether to get TD or normal methods
*
* @return TLConstructor
*/
public function getConstructors(bool $td = false): TLConstructor
{
return $td ? $this->td_constructors : $this->constructors;
}
/**
* Get methods.
*
* @param int $td Whether to get TD or normal methods
*
* @return TLMethod
*/
public function getMethods(bool $td = false): TLMethod
{
return $td ? $this->td_methods : $this->methods;
}
/**
* Get TL descriptions.
*
* @return array
*/
public function getDescriptions(): array
{
return $this->td_descriptions;
}
/**
* Initialize TL parser.
*
* @param array $files Scheme files
* @param TLCallback[] $objects TL Callback objects
*
* @return void
*/
public function init(array $files, array $objects = [])
{
$this->API->logger->logger(\danog\MadelineProto\Lang::$current_lang['TL_loading'], \danog\MadelineProto\Logger::VERBOSE);
$this->updateCallbacks($objects); $this->updateCallbacks($objects);
$this->constructors = new TLConstructor(); $this->constructors = new TLConstructor();
$this->methods = new TLMethod(); $this->methods = new TLMethod();
@ -92,7 +146,7 @@ class TL
$this->td_methods = new TLMethod(); $this->td_methods = new TLMethod();
$this->td_descriptions = ['types' => [], 'constructors' => [], 'methods' => []]; $this->td_descriptions = ['types' => [], 'constructors' => [], 'methods' => []];
foreach ($files as $scheme_type => $file) { foreach ($files as $scheme_type => $file) {
$this->logger->logger(\sprintf(\danog\MadelineProto\Lang::$current_lang['file_parsing'], \basename($file)), \danog\MadelineProto\Logger::VERBOSE); $this->API->logger->logger(\sprintf(\danog\MadelineProto\Lang::$current_lang['file_parsing'], \basename($file)), \danog\MadelineProto\Logger::VERBOSE);
$filec = \file_get_contents(\danog\MadelineProto\Absolute::absolute($file)); $filec = \file_get_contents(\danog\MadelineProto\Absolute::absolute($file));
$TL_dict = \json_decode($filec, true); $TL_dict = \json_decode($filec, true);
if ($TL_dict === null) { if ($TL_dict === null) {
@ -165,7 +219,7 @@ class TL
if (\preg_match('/^[^\s]+#([a-f0-9]*)/i', $line, $matches)) { if (\preg_match('/^[^\s]+#([a-f0-9]*)/i', $line, $matches)) {
$nid = \str_pad($matches[1], 8, '0', \STR_PAD_LEFT); $nid = \str_pad($matches[1], 8, '0', \STR_PAD_LEFT);
if ($id !== $nid && $scheme_type !== 'botAPI') { if ($id !== $nid && $scheme_type !== 'botAPI') {
$this->logger->logger(\sprintf(\danog\MadelineProto\Lang::$current_lang['crc32_mismatch'], $id, $nid, $line), \danog\MadelineProto\Logger::ERROR); $this->API->logger->logger(\sprintf(\danog\MadelineProto\Lang::$current_lang['crc32_mismatch'], $id, $nid, $line), \danog\MadelineProto\Logger::ERROR);
} }
$id = $nid; $id = $nid;
} }
@ -210,15 +264,14 @@ class TL
if (empty($TL_dict) || empty($TL_dict['constructors']) || !isset($TL_dict['methods'])) { if (empty($TL_dict) || empty($TL_dict['constructors']) || !isset($TL_dict['methods'])) {
throw new Exception(\danog\MadelineProto\Lang::$current_lang['src_file_invalid'].$file); throw new Exception(\danog\MadelineProto\Lang::$current_lang['src_file_invalid'].$file);
} }
$orig = $this->encrypted_layer; $this->API->logger->logger(\danog\MadelineProto\Lang::$current_lang['translating_obj'], \danog\MadelineProto\Logger::ULTRA_VERBOSE);
$this->logger->logger(\danog\MadelineProto\Lang::$current_lang['translating_obj'], \danog\MadelineProto\Logger::ULTRA_VERBOSE);
foreach ($TL_dict['constructors'] as $elem) { foreach ($TL_dict['constructors'] as $elem) {
if ($scheme_type === 'secret') { if ($scheme_type === 'secret') {
$this->encrypted_layer = \max($this->encrypted_layer, $elem['layer']); $this->encrypted_layer = \max($this->encrypted_layer, $elem['layer']);
} }
$this->{($scheme_type === 'td' ? 'td_' : '').'constructors'}->add($elem, $scheme_type); $this->{($scheme_type === 'td' ? 'td_' : '').'constructors'}->add($elem, $scheme_type);
} }
$this->logger->logger(\danog\MadelineProto\Lang::$current_lang['translating_methods'], \danog\MadelineProto\Logger::ULTRA_VERBOSE); $this->API->logger->logger(\danog\MadelineProto\Lang::$current_lang['translating_methods'], \danog\MadelineProto\Logger::ULTRA_VERBOSE);
foreach ($TL_dict['methods'] as $elem) { foreach ($TL_dict['methods'] as $elem) {
$this->{($scheme_type === 'td' ? 'td_' : '').'methods'}->add($elem); $this->{($scheme_type === 'td' ? 'td_' : '').'methods'}->add($elem);
if ($scheme_type === 'secret') { if ($scheme_type === 'secret') {
@ -253,7 +306,12 @@ class TL
} }
} }
public function getMethodNamespaces() /**
* Get TL namespaces.
*
* @return array
*/
public function getMethodNamespaces(): array
{ {
$res = []; $res = [];
foreach ($this->methods->method_namespace as $pair) { foreach ($this->methods->method_namespace as $pair) {
@ -264,16 +322,28 @@ class TL
return $res; return $res;
} }
public function getMethodsNamespaced() /**
* Get namespaced methods (method => namespace).
*
* @return array
*/
public function getMethodsNamespaced(): array
{ {
return $this->methods->method_namespace; return $this->methods->method_namespace;
} }
public function updateCallbacks($objects) /**
* Update TL callbacks.
*
* @param TLCallback[] $objects TL callbacks
*
* @return void
*/
public function updateCallbacks(array $objects)
{ {
$this->tl_callbacks = []; $this->tl_callbacks = [];
foreach ($objects as $object) { foreach ($objects as $object) {
if (!isset(\class_implements(\get_class($object))['danog\\MadelineProto\\TL\\TLCallback'])) { if (!isset(\class_implements(\get_class($object))[TLCallback::class])) {
throw new Exception('Invalid callback object provided!'); throw new Exception('Invalid callback object provided!');
} }
$new = [ $new = [
@ -298,8 +368,14 @@ class TL
} }
} }
} }
/**
public function deserializeBool($id) * Deserialize bool.
*
* @param string $id Constructor ID
*
* @return bool
*/
private function deserializeBool(string $id): bool
{ {
$tl_elem = $this->constructors->findById($id); $tl_elem = $this->constructors->findById($id);
if ($tl_elem === false) { if ($tl_elem === false) {
@ -309,7 +385,17 @@ class TL
return $tl_elem['predicate'] === 'boolTrue'; return $tl_elem['predicate'] === 'boolTrue';
} }
public function serializeObject($type, $object, $ctx, $layer = -1) /**
* Serialize TL object.
*
* @param array $type TL type definition
* @param mixed $object Object to serialize
* @param string $ctx Context
* @param integer $layer Layer version
*
* @return \Generator<string>
*/
public function serializeObject(array $type, $object, $ctx, int $layer = -1): \Generator
{ {
switch ($type['type']) { switch ($type['type']) {
case 'int': case 'int':
@ -464,7 +550,7 @@ class TL
$predicate = $object['_']; $predicate = $object['_'];
$constructorData = $this->constructors->findByPredicate($predicate, $layer); $constructorData = $this->constructors->findByPredicate($predicate, $layer);
if ($constructorData === false) { if ($constructorData === false) {
$this->logger->logger($object, \danog\MadelineProto\Logger::FATAL_ERROR); $this->API->logger->logger($object, \danog\MadelineProto\Logger::FATAL_ERROR);
throw new Exception(\sprintf(\danog\MadelineProto\Lang::$current_lang['type_extract_error'], $predicate)); throw new Exception(\sprintf(\danog\MadelineProto\Lang::$current_lang['type_extract_error'], $predicate));
} }
@ -486,7 +572,15 @@ class TL
return $concat.yield $this->serializeParams($constructorData, $object, '', $layer); return $concat.yield $this->serializeParams($constructorData, $object, '', $layer);
} }
public function serializeMethod($method, $arguments) /**
* Serialize method.
*
* @param string $method Method name
* @param mixed $arguments Arguments
*
* @return \Generator<string>
*/
public function serializeMethod(string $method, $arguments): \Generator
{ {
if ($method === 'messages.importChatInvite' && isset($arguments['hash']) && \is_string($arguments['hash']) && \preg_match('@(?:t|telegram)\.(?:me|dog)/(joinchat/)?([a-z0-9_-]*)@i', $arguments['hash'], $matches)) { if ($method === 'messages.importChatInvite' && isset($arguments['hash']) && \is_string($arguments['hash']) && \preg_match('@(?:t|telegram)\.(?:me|dog)/(joinchat/)?([a-z0-9_-]*)@i', $arguments['hash'], $matches)) {
if ($matches[1] === '') { if ($matches[1] === '') {
@ -523,7 +617,7 @@ class TL
) && ) &&
$this->settings['upload']['allow_automatic_upload'] $this->settings['upload']['allow_automatic_upload']
) { ) {
$arguments['file'] = yield $this->uploadEncrypted($arguments['file']); $arguments['file'] = yield $this->API->uploadEncrypted($arguments['file']);
} }
if (isset($arguments['file']['key'])) { if (isset($arguments['file']['key'])) {
$arguments['message']['media']['key'] = $arguments['file']['key']; $arguments['message']['media']['key'] = $arguments['file']['key'];
@ -533,7 +627,7 @@ class TL
} }
} }
} elseif (\in_array($method, ['messages.addChatUser', 'messages.deleteChatUser', 'messages.editChatAdmin', 'messages.editChatPhoto', 'messages.editChatTitle', 'messages.getFullChat', 'messages.exportChatInvite', 'messages.editChatAdmin', 'messages.migrateChat']) && isset($arguments['chat_id']) && (!\is_numeric($arguments['chat_id']) || $arguments['chat_id'] < 0)) { } elseif (\in_array($method, ['messages.addChatUser', 'messages.deleteChatUser', 'messages.editChatAdmin', 'messages.editChatPhoto', 'messages.editChatTitle', 'messages.getFullChat', 'messages.exportChatInvite', 'messages.editChatAdmin', 'messages.migrateChat']) && isset($arguments['chat_id']) && (!\is_numeric($arguments['chat_id']) || $arguments['chat_id'] < 0)) {
$res = yield $this->getInfo($arguments['chat_id']); $res = yield $this->API->getInfo($arguments['chat_id']);
if ($res['type'] !== 'chat') { if ($res['type'] !== 'chat') {
throw new \danog\MadelineProto\Exception('chat_id is not a chat id (only normal groups allowed, not supergroups)!'); throw new \danog\MadelineProto\Exception('chat_id is not a chat id (only normal groups allowed, not supergroups)!');
} }
@ -566,10 +660,20 @@ class TL
return $tl['id'].yield $this->serializeParams($tl, $arguments, $method); return $tl['id'].yield $this->serializeParams($tl, $arguments, $method);
} }
public function serializeParams($tl, $arguments, $ctx, $layer = -1) /**
* Serialize parameters.
*
* @param array $tl TL object definition
* @param arrayLike $arguments Arguments
* @param string $ctx Context
* @param integer $layer Layer
*
* @return \Generator<string>
*/
private function serializeParams(array $tl, $arguments, $ctx, int $layer = -1): \Generator
{ {
$serialized = ''; $serialized = '';
$arguments = yield $this->botAPIToMTProto($arguments); $arguments = yield $this->API->botAPIToMTProto($arguments);
$flags = 0; $flags = 0;
foreach ($tl['params'] as $cur_flag) { foreach ($tl['params'] as $cur_flag) {
if (isset($cur_flag['pow'])) { if (isset($cur_flag['pow'])) {
@ -595,7 +699,7 @@ class TL
foreach ($tl['params'] as $current_argument) { foreach ($tl['params'] as $current_argument) {
if (!isset($arguments[$current_argument['name']])) { if (!isset($arguments[$current_argument['name']])) {
if (isset($current_argument['pow']) && (\in_array($current_argument['type'], ['true', 'false']) || ($flags & $current_argument['pow']) === 0)) { if (isset($current_argument['pow']) && (\in_array($current_argument['type'], ['true', 'false']) || ($flags & $current_argument['pow']) === 0)) {
//$this->logger->logger('Skipping '.$current_argument['name'].' of type '.$current_argument['type'); //$this->API->logger->logger('Skipping '.$current_argument['name'].' of type '.$current_argument['type');
continue; continue;
} }
if ($current_argument['name'] === 'random_bytes') { if ($current_argument['name'] === 'random_bytes') {
@ -603,7 +707,7 @@ class TL
continue; continue;
} }
if ($current_argument['name'] === 'data' && isset($tl['method']) && \in_array($tl['method'], ['messages.sendEncrypted', 'messages.sendEncryptedFile', 'messages.sendEncryptedService']) && isset($arguments['message'])) { if ($current_argument['name'] === 'data' && isset($tl['method']) && \in_array($tl['method'], ['messages.sendEncrypted', 'messages.sendEncryptedFile', 'messages.sendEncryptedService']) && isset($arguments['message'])) {
$serialized .= yield $this->serializeObject($current_argument, yield $this->encryptSecretMessage($arguments['peer']['chat_id'], $arguments['message']), 'data'); $serialized .= yield $this->serializeObject($current_argument, yield $this->API->encryptSecretMessage($arguments['peer']['chat_id'], $arguments['message']), 'data');
continue; continue;
} }
if ($current_argument['name'] === 'random_id') { if ($current_argument['name'] === 'random_id') {
@ -691,27 +795,35 @@ class TL
) )
&& $this->settings['upload']['allow_automatic_upload'] && $this->settings['upload']['allow_automatic_upload']
) { ) {
$arguments[$current_argument['name']] = yield $this->upload($arguments[$current_argument['name']]); $arguments[$current_argument['name']] = yield $this->API->upload($arguments[$current_argument['name']]);
} }
if ($current_argument['type'] === 'InputEncryptedChat' && (!\is_array($arguments[$current_argument['name']]) || isset($arguments[$current_argument['name']]['_']) && $this->constructors->findByPredicate($arguments[$current_argument['name']]['_'])['type'] !== $current_argument['type'])) { if ($current_argument['type'] === 'InputEncryptedChat' && (!\is_array($arguments[$current_argument['name']]) || isset($arguments[$current_argument['name']]['_']) && $this->constructors->findByPredicate($arguments[$current_argument['name']]['_'])['type'] !== $current_argument['type'])) {
if (\is_array($arguments[$current_argument['name']])) { if (\is_array($arguments[$current_argument['name']])) {
$arguments[$current_argument['name']] = (yield $this->getInfo($arguments[$current_argument['name']]))['InputEncryptedChat']; $arguments[$current_argument['name']] = (yield $this->API->getInfo($arguments[$current_argument['name']]))['InputEncryptedChat'];
} else { } else {
if (!isset($this->secret_chats[$arguments[$current_argument['name']]])) { if (!$this->API->hasSecretChat($arguments[$current_argument['name']])) {
throw new \danog\MadelineProto\Exception(\danog\MadelineProto\Lang::$current_lang['sec_peer_not_in_db']); throw new \danog\MadelineProto\Exception(\danog\MadelineProto\Lang::$current_lang['sec_peer_not_in_db']);
} }
$arguments[$current_argument['name']] = $this->secret_chats[$arguments[$current_argument['name']]]['InputEncryptedChat']; $arguments[$current_argument['name']] = $this->API->getSecretChat($arguments[$current_argument['name']])['InputEncryptedChat'];
} }
} }
//$this->logger->logger('Serializing '.$current_argument['name'].' of type '.$current_argument['type'); //$this->API->logger->logger('Serializing '.$current_argument['name'].' of type '.$current_argument['type');
$serialized .= yield $this->serializeObject($current_argument, $arguments[$current_argument['name']], $current_argument['name'], $layer); $serialized .= yield $this->serializeObject($current_argument, $arguments[$current_argument['name']], $current_argument['name'], $layer);
} }
return $serialized; return $serialized;
} }
public function getLength($stream, $type = ['type' => '']) /**
* Get length of TL payload.
*
* @param resource $stream Stream
* @param array $type Type identifier
*
* @return int
*/
public function getLength($stream, $type = ['type' => '']): int
{ {
if (\is_string($stream)) { if (\is_string($stream)) {
$res = \fopen('php://memory', 'rw+b'); $res = \fopen('php://memory', 'rw+b');
@ -726,8 +838,14 @@ class TL
return \ftell($stream); return \ftell($stream);
} }
/** /**
* :type stream: io.BytesIO object. * Deserialize TL object.
*
* @param resource $stream Stream
* @param array $type Type identifier
*
* @return mixed
*/ */
public function deserialize($stream, $type = ['type' => '']) public function deserialize($stream, $type = ['type' => ''])
{ {

View File

@ -96,7 +96,14 @@ class TLConstructor
return false; return false;
} }
public function findById($id) /**
* Find constructor by ID.
*
* @param string $id Constructor ID
*
* @return array|false
*/
public function findById(string $id)
{ {
if (isset($this->by_id[$id])) { if (isset($this->by_id[$id])) {
$constructor = $this->by_id[$id]; $constructor = $this->by_id[$id];

View File

@ -22,7 +22,7 @@ namespace danog\MadelineProto\Wrappers;
trait DialogHandler trait DialogHandler
{ {
/** /**
* Get dialog peers * Get dialog peers.
* *
* @param boolean $force Whether to refetch all dialogs ignoring cache * @param boolean $force Whether to refetch all dialogs ignoring cache
* *
@ -47,7 +47,7 @@ trait DialogHandler
} }
/** /**
* Get full info of all dialogs * Get full info of all dialogs.
* *
* @param boolean $force Whether to refetch all dialogs ignoring cache * @param boolean $force Whether to refetch all dialogs ignoring cache
* *

View File

@ -22,31 +22,31 @@ namespace danog\MadelineProto\Wrappers;
use EventHandler; use EventHandler;
/** /**
* Event handler * Event handler.
*/ */
trait Events trait Events
{ {
/** /**
* Event handler class name * Event handler class name.
* *
* @var string * @var string
*/ */
public $event_handler; public $event_handler;
/** /**
* Event handler instance * Event handler instance.
* *
* @var \danog\MadelineProto\EventHandler * @var \danog\MadelineProto\EventHandler
*/ */
private $event_handler_instance; private $event_handler_instance;
/** /**
* Event handler method list * Event handler method list.
* *
* @var array<string> * @var array<string>
*/ */
private $event_handler_methods = []; private $event_handler_methods = [];
/** /**
* Set event handler * Set event handler.
* *
* @param string|EventHandler $event_handler Event handler * @param string|EventHandler $event_handler Event handler
* *
@ -91,7 +91,7 @@ trait Events
} }
/** /**
* Get event handler * Get event handler.
* *
* @return EventHandler * @return EventHandler
*/ */
@ -101,7 +101,7 @@ trait Events
} }
/** /**
* Event update handler * Event update handler.
* *
* @param array $update Update * @param array $update Update
* *

View File

@ -44,7 +44,7 @@ trait Login
yield $this->logout(); yield $this->logout();
} }
$callbacks = [$this, $this->referenceDatabase]; $callbacks = [$this, $this->referenceDatabase];
$this->updateCallbacks($callbacks); $this->TL->updateCallbacks($callbacks);
$this->logger->logger(\danog\MadelineProto\Lang::$current_lang['login_bot'], \danog\MadelineProto\Logger::NOTICE); $this->logger->logger(\danog\MadelineProto\Lang::$current_lang['login_bot'], \danog\MadelineProto\Logger::NOTICE);
$this->authorization = yield $this->methodCallAsyncRead('auth.importBotAuthorization', ['bot_auth_token' => $token, 'api_id' => $this->settings['app_info']['api_id'], 'api_hash' => $this->settings['app_info']['api_hash']], ['datacenter' => $this->datacenter->curdc]); $this->authorization = yield $this->methodCallAsyncRead('auth.importBotAuthorization', ['bot_auth_token' => $token, 'api_id' => $this->settings['app_info']['api_id'], 'api_hash' => $this->settings['app_info']['api_hash']], ['datacenter' => $this->datacenter->curdc]);
@ -159,7 +159,7 @@ trait Login
if (!($this->authorization['user']['bot'] ?? false)) { if (!($this->authorization['user']['bot'] ?? false)) {
$callbacks []= $this->minDatabase; $callbacks []= $this->minDatabase;
} }
$this->updateCallbacks($callbacks); $this->TL->updateCallbacks($callbacks);
$this->startUpdateSystem(); $this->startUpdateSystem();

View File

@ -25,7 +25,7 @@ namespace danog\MadelineProto\Wrappers;
trait TOS trait TOS
{ {
/** /**
* Check for terms of service update * Check for terms of service update.
* *
* @return \Generator * @return \Generator
*/ */
@ -52,7 +52,7 @@ trait TOS
} }
/** /**
* Accept terms of service update * Accept terms of service update.
* *
* @return \Generator * @return \Generator
*/ */
@ -67,7 +67,7 @@ trait TOS
} }
/** /**
* Decline terms of service update * Decline terms of service update.
* *
* THIS WILL DELETE YOUR ACCOUNT! * THIS WILL DELETE YOUR ACCOUNT!
* *