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;
use Amp\Promise;
use danog\MadelineProto\TL\TL;
use phpDocumentor\Reflection\DocBlockFactory;
class AnnotationsBuilder
{
use \danog\MadelineProto\TL\TL;
use Tools;
public function __construct($logger, $settings)
{
$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;
}
@ -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);
}
\file_put_contents($filename, $content);
@ -87,12 +93,12 @@ class AnnotationsBuilder
$handle = \fopen(\dirname(__FILE__).'/InternalDoc.php', 'w');
$internalDoc = [];
foreach ($this->methods->by_id as $id => $data) {
foreach ($this->TL->getMethods()->by_id as $id => $data) {
if (!\strpos($data['method'], '.')) {
continue;
}
list($namespace, $method) = \explode('.', $data['method']);
if (!\in_array($namespace, $this->getMethodNamespaces())) {
if (!\in_array($namespace, $this->TL->getMethodNamespaces())) {
continue;
}
$internalDoc[$namespace][$method]['title'] = Lang::$current_lang["method_{$data['method']}"] ?? '';

View File

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

View File

@ -19,10 +19,11 @@
namespace danog\MadelineProto;
use danog\MadelineProto\TL\TL;
// This code was written a few years ago: it is garbage, and has to be rewritten
class DocsBuilder
{
use \danog\MadelineProto\TL\TL;
use \danog\MadelineProto\DocsBuilder\Methods;
use \danog\MadelineProto\DocsBuilder\Constructors;
use Tools;
@ -32,10 +33,14 @@ class DocsBuilder
{
$this->logger = $logger;
\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'])) {
$this->constructors = $this->td_constructors;
$this->methods = $this->td_methods;
$this->td = true;
}
$this->settings = $settings;

View File

@ -35,15 +35,15 @@ trait Constructors
$this->docs_constructors = [];
$this->logger->logger('Generating constructors documentation...', \danog\MadelineProto\Logger::NOTICE);
$got = [];
foreach ($this->constructors->by_predicate_and_layer as $predicate => $id) {
$data = $this->constructors->by_id[$id];
foreach ($this->TL->getConstructors($this->td)->by_predicate_and_layer as $predicate => $id) {
$data = $this->TL->getConstructors($this->td)->by_id[$id];
if (isset($got[$id])) {
$data['layer'] = '';
}
$got[$id] = '';
/*
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'] : '';
$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';
$param[$type_or_subtype] = \str_replace(['.', 'true', 'false'], ['_', 'Bool', 'Bool'], $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) === '>') {
$param[$type_or_subtype] = \substr($param[$type_or_subtype], 0, -1);
@ -87,14 +87,14 @@ trait Constructors
| Name | Type | Required |
|----------|---------------|----------|
';
if (!isset($this->td_descriptions['constructors'][$data['predicate']])) {
if (!isset($this->TL->getDescriptions()['constructors'][$data['predicate']])) {
$this->addToLang('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:
| Name | Type | Required | Description |
@ -126,7 +126,7 @@ trait Constructors
}
}*/
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';
if (\substr($ptype, -1) === '>') {
@ -156,16 +156,16 @@ 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->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']);
if (isset($this->td_descriptions['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']];
if (isset($this->TL->getDescriptions()['constructors'][$data['predicate']]['description'])) {
$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']])) {
$table .= $this->td_descriptions['constructors'][$data['predicate']]['params'][$param['name']].'|';
if (isset($this->TL->getDescriptions()['constructors'][$data['predicate']]['params'][$param['name']])) {
$table .= $this->TL->getDescriptions()['constructors'][$data['predicate']]['params'][$param['name']].'|';
}
$table .= PHP_EOL;
$pptype = \in_array($ptype, ['string', 'bytes']) ? "'".$ptype."'" : $ptype;
@ -183,7 +183,7 @@ trait Constructors
$params = "['_' => '".$data['predicate']."'".$params.']';
$lua_params = "{_='".$data['predicate']."'".$lua_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 = '---
title: '.$data['predicate'].'
description: '.$description.'
@ -199,8 +199,8 @@ image: https://docs.madelineproto.xyz/favicons/android-chrome-256x256.png
';
if (isset($this->td_descriptions['constructors'][$data['predicate']])) {
$header .= $this->td_descriptions['constructors'][$data['predicate']]['description'].PHP_EOL.PHP_EOL;
if (isset($this->TL->getDescriptions()['constructors'][$data['predicate']])) {
$header .= $this->TL->getDescriptions()['constructors'][$data['predicate']]['description'].PHP_EOL.PHP_EOL;
}
$type = '### Type: ['.\str_replace('_', '\\_', $php_type).'](../types/'.$php_type.'.md)

View File

@ -54,7 +54,7 @@ trait Methods
$this->docs_methods = [];
$this->human_docs_methods = [];
$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'];
$php_method = \str_replace('.', '->', $data['method']);
$type = \str_replace(['.', '<', '>'], ['_', '_of_', ''], $data['type']);
@ -170,9 +170,9 @@ trait Methods
}
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 {
$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;
$pptype = \in_array($ptype, ['string', 'bytes']) ? "'".$ptype."'" : $ptype;

View File

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

View File

@ -4040,6 +4040,33 @@ class InternalDoc extends APIFactory
{
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.
*
@ -4078,11 +4105,6 @@ class InternalDoc extends APIFactory
{
return $this->__call(__FUNCTION__, [$extra]);
}
public function hasAllAuth(array $extra = []): bool
{
return $this->__call(__FUNCTION__, [$extra]);
}
/**
* Get correct settings array for the latest version.
*
@ -4139,7 +4161,20 @@ class InternalDoc extends APIFactory
{
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 = [])
{
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]);
}
/**
* Accept secret chat.
*
* @param array $params Secret chat ID
*
* @return \Generator
*/
public function acceptSecretChat($params, array $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 = [])
{
return $this->__call(__FUNCTION__, [$user, $extra]);
}
public function completeSecretChat($params, array $extra = [])
{
return $this->__call(__FUNCTION__, [$params, $extra]);
}
public function notifyLayer($chat, array $extra = [])
{
return $this->__call(__FUNCTION__, [$chat, $extra]);
}
/**
* Rekey secret chat.
*
* @param mixed $chat Secret chat to rekey
*
* @return \Generator
*/
public function rekey($chat, array $extra = [])
{
return $this->__call(__FUNCTION__, [$chat, $extra]);
}
public function acceptRekey($chat, $params, array $extra = [])
{
return $this->__call(__FUNCTION__, [$chat, $params, $extra]);
}
public function commitRekey($chat, $params, array $extra = [])
{
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 = [])
/**
* 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, array $extra = []): int
{
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]);
}
/**
* 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 = [])
{
return $this->__call(__FUNCTION__, [$chat, $extra]);
@ -4790,63 +4847,6 @@ class InternalDoc extends APIFactory
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 = [])
{
return $this->__call(__FUNCTION__, [$stuff, $extra]);
@ -5442,6 +5442,19 @@ class InternalDoc extends APIFactory
{
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 = [])
{
@ -5483,28 +5496,58 @@ class InternalDoc extends APIFactory
{
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 = [])
{
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 = [])
{
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 = [])
{
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]);
}
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]);
}
@ -5645,17 +5688,31 @@ class InternalDoc extends APIFactory
{
return $this->__call(__FUNCTION__, [$template, $extra]);
}
/**
* Check for terms of service update.
*
* @return \Generator
*/
public function checkTos(array $extra = [])
{
return $this->__call(__FUNCTION__, [$extra]);
}
/**
* Accept terms of service update.
*
* @return \Generator
*/
public function acceptTos(array $extra = [])
{
return $this->__call(__FUNCTION__, [$extra]);
}
/**
* Decline terms of service update.
*
* THIS WILL DELETE YOUR ACCOUNT!
*
* @return \Generator
*/
public function declineTos(array $extra = [])
{
return $this->__call(__FUNCTION__, [$extra]);

View File

@ -182,7 +182,7 @@ class WriteLoop extends ResumableSignalLoop
$temporary_keys = [];
if (\count($to_ack = $connection->ack_queue)) {
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;
$API->logger->logger("Adding msgs_ack {$connection->pending_outgoing_key}", Logger::ULTRA_VERBOSE);
$connection->pending_outgoing_key++;
@ -200,7 +200,7 @@ class WriteLoop extends ResumableSignalLoop
}
if ($shared->isHttp() && !$has_http_wait) {
$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;
$connection->pending_outgoing_key++;
}
@ -240,11 +240,11 @@ class WriteLoop extends ResumableSignalLoop
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->serializeMethod(
$MTmessage['body'] = yield $API->getTL()->serializeMethod(
'invokeWithLayer',
[
'layer' => $API->settings['tl_schema']['layer'],
'query' => yield $API->serializeMethod(
'query' => yield $API->getTL()->serializeMethod(
'initConnection',
[
'api_id' => $API->settings['app_info']['api_id'],
@ -266,7 +266,7 @@ class WriteLoop extends ResumableSignalLoop
if (!isset($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;
if (\count($connection->call_queue[$message['queue']]) > $API->settings['msg_array_limit']['call_queue']) {
@ -278,7 +278,7 @@ class WriteLoop extends ResumableSignalLoop
// TODO
/* if ($API->settings['requests']['gzip_encode_if_gt'] !== -1 && ($l = strlen($MTmessage['body'])) > $API->settings['requests']['gzip_encode_if_gt']) {
if (($g = strlen($gzipped = gzencode($MTmessage['body']))) < $l) {
$MTmessage['body'] = yield $API->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);
}
unset($gzipped);
@ -317,7 +317,7 @@ class WriteLoop extends ResumableSignalLoop
//var_dumP("container ".bin2hex($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);
$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\ReferenceDatabase;
use danog\MadelineProto\MTProtoTools\UpdatesState;
use danog\MadelineProto\TL\TL;
use danog\MadelineProto\TL\TLCallback;
/**
@ -49,7 +50,6 @@ class MTProto extends AsyncConstruct implements TLCallback
use \danog\MadelineProto\SecretChats\MessageHandler;
use \danog\MadelineProto\SecretChats\ResponseHandler;
use \danog\MadelineProto\SecretChats\SeqNoHandler;
use \danog\MadelineProto\TL\TL;
use \danog\MadelineProto\TL\Conversion\BotAPI;
use \danog\MadelineProto\TL\Conversion\BotAPIFiles;
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',
128 => ' and other party knows for a fact that message is already received'
];
const REQUESTED = 0;
const ACCEPTED = 1;
const CONFIRMED = 2;
const READY = 3;
/**
* Secret chat was not found.
*
* @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_REVERSE = ['sendMessage' => 'messages.sendMessage'];
const TD_IGNORE = ['updateMessageID'];
@ -419,6 +433,13 @@ class MTProto extends AsyncConstruct implements TLCallback
*/
public $logger;
/**
* TL serializer.
*
* @var \danog\MadelineProto\TL\TL
*/
private $TL;
/**
* Constructor function.
*
@ -449,19 +470,17 @@ class MTProto extends AsyncConstruct implements TLCallback
$this->logger->logger(Lang::$current_lang['load_rsa'], Logger::ULTRA_VERBOSE);
$this->rsa_keys = [];
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;
}
/*
* ***********************************************************************
* Define some needed numbers for BigInteger
*/
// (re)-initialize TL
$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->constructTL($this->settings['tl_schema']['src'], $callbacks);
$this->TL->init($this->settings['tl_schema']['src'], $callbacks);
yield $this->connectToAllDcs();
$this->startLoops();
$this->datacenter->curdc = 2;
@ -495,72 +514,66 @@ class MTProto extends AsyncConstruct implements TLCallback
}
return [
// Databases
'chats',
'full_chats',
'referenceDatabase',
'minDatabase',
'channel_participants',
'chats',
'full_chats',
'referenceDatabase',
'minDatabase',
'channel_participants',
// Misc caching
'dialog_params',
'last_stored',
'qres',
'supportUser',
'dialog_params',
'last_stored',
'qres',
'supportUser',
'tos',
// Event handler
'event_handler',
'event_handler_instance',
'event_handler',
'event_handler_instance',
'loop_callback',
'updates',
'updates_key',
'updates',
'updates_key',
'hook_url',
// Web login template
'web_template',
'web_template',
// Settings
'settings',
'config',
'settings',
'config',
// Authorization keys
'datacenter',
'datacenter',
// Authorization state
'authorization',
'authorized',
'authorized_dc',
'authorization',
'authorized',
'authorized_dc',
// Authorization cache
'rsa_keys',
'dh_config',
'rsa_keys',
'dh_config',
// Update state
'got_state',
'channels_state',
'msg_ids',
'got_state',
'channels_state',
'msg_ids',
// Version
'v',
'v',
// TL (we don't need this)
'constructors',
'td_constructors',
'methods',
'td_methods',
'td_descriptions',
'tl_callbacks',
// TL
'TL',
// Secret chats
'secret_chats',
'encrypted_layer',
'temp_requested_secret_chats',
'temp_rekeyed_secret_chats',
'secret_chats',
'temp_requested_secret_chats',
'temp_rekeyed_secret_chats',
// Object storage
'storage',
];
'storage',
];
}
@ -576,7 +589,7 @@ class MTProto extends AsyncConstruct implements TLCallback
if (!($this->authorization['user']['bot'] ?? false)) {
$callbacks []= $this->minDatabase;
}
$this->updateCallbacks($callbacks);
$this->TL->updateCallbacks($callbacks);
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);
}
/**
* 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.
*
@ -736,9 +780,18 @@ class MTProto extends AsyncConstruct implements TLCallback
if (!isset($this->minDatabase)) {
$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
*/
@ -863,7 +916,7 @@ class MTProto extends AsyncConstruct implements TLCallback
if (!($this->authorization['user']['bot'] ?? false)) {
$callbacks []= $this->minDatabase;
}
$this->updateCallbacks($callbacks);
$this->TL->updateCallbacks($callbacks);
$this->settings['connection_settings']['all']['ipv6'] = Magic::$ipv6;
if ($this->authorized === true) {
@ -1350,6 +1403,11 @@ class MTProto extends AsyncConstruct implements TLCallback
return $this->datacenter->isHttp($datacenter);
}
/**
* Checks whether all datacenters are authorized.
*
* @return boolean
*/
public function hasAllAuth(): bool
{
if ($this->isInitingAuthorization()) {
@ -1365,6 +1423,11 @@ class MTProto extends AsyncConstruct implements TLCallback
return true;
}
/**
* Whether we're initing authorization.
*
* @return boolean
*/
public function isInitingAuthorization()
{
return $this->initing_authorization;
@ -1563,8 +1626,7 @@ class MTProto extends AsyncConstruct implements TLCallback
{
try {
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] = $tempkey;
$this->cdn_rsa_keys[$tempkey->fp] = yield (new RSA)->load($this->TL, $curkey['public_key']);
}
} catch (\danog\MadelineProto\TL\Exception $e) {
$this->logger->logger($e->getMessage(), \danog\MadelineProto\Logger::FATAL_ERROR);

View File

@ -186,7 +186,7 @@ trait AuthKeyHandler
$q_bytes = $q->toBytes();
$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)];
$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
@ -265,12 +265,12 @@ trait AuthKeyHandler
* int $server_time
* ]
*/
$server_DH_inner_data = $this->deserialize($answer, ['type' => '']);
$server_DH_inner_data = $this->TL->deserialize($answer, ['type' => '']);
/*
* ***********************************************************************
* 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) {
throw new \danog\MadelineProto\SecurityException('answer_hash mismatch.');
}
@ -320,7 +320,7 @@ trait AuthKeyHandler
* 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
@ -550,7 +550,7 @@ trait AuthKeyHandler
$temp_auth_key_id = $datacenterConnection->getTempAuthKey()->getID();
$perm_auth_key_id = $datacenterConnection->getPermAuthKey()->getID();
$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();
$seq_no = 0;
$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;
use danog\MadelineProto\TL\TL;
/**
* RSA class
* RSA class.
*/
class RSA
{
use \danog\MadelineProto\TL\TL;
use \danog\MadelineProto\Tools;
use \danog\Serializable;
/**
* Exponent
* Exponent.
*
* @var \phpseclib\Math\BigInteger
*/
public $e;
/**
* Modulus
* Modulus.
*
* @var \phpseclib\Math\BigInteger
*/
public $n;
/**
* Fingerprint
* Fingerprint.
*
* @var string
*/
public $fp;
/**
* Load RSA key
* Load RSA key.
*
* @param TL $TL TL serializer
* @param string $rsa_key RSA key
*
*
* @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['loading_key'], Logger::ULTRA_VERBOSE);
@ -60,13 +62,13 @@ 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 $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;
}
/**
* Sleep function
* Sleep function.
*
* @return array
*/
@ -76,10 +78,10 @@ class RSA
}
/**
* Encrypt data
* Encrypt data.
*
* @param string $data Data to encrypt
*
*
* @return string
*/
public function encrypt($data): string

View File

@ -19,6 +19,8 @@
namespace danog\MadelineProto\SecretChats;
use danog\MadelineProto\MTProto;
/**
* Manages secret chats.
*
@ -26,10 +28,27 @@ namespace danog\MadelineProto\SecretChats;
*/
trait AuthKeyHandler
{
/**
* Temporary requested secret chats.
*
* @var array
*/
protected $temp_requested_secret_chats = [];
/**
* Secret chats.
*
* @var array
*/
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']));
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);
}
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);
if (!isset($user['InputUser'])) {
@ -78,7 +104,14 @@ trait AuthKeyHandler
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) {
//$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);
}
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 = [];
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) {
return;
@ -133,7 +178,15 @@ trait AuthKeyHandler
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) {
$my_exchange_id = new \phpseclib\Math\BigInteger($this->secret_chats[$chat]['rekeying'][1], -256);
@ -166,7 +219,15 @@ trait AuthKeyHandler
$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']])) {
$this->secret_chats[$chat]['rekeying'] = [0];
@ -196,7 +257,15 @@ trait AuthKeyHandler
$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'])) {
return;
@ -219,27 +288,59 @@ trait AuthKeyHandler
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])) {
return 2;
return MTProto::SECRET_READY;
}
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];
}
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(debug_backtrace(0)[0]);
if (isset($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]['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;
if ($this->secret_chats[$chat_id]['mtproto'] === 2) {
$padding = \danog\MadelineProto\Tools::posmod(-\strlen($message), 16);
@ -111,7 +111,7 @@ trait MessageHandler
$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']--;
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']);

View File

@ -374,7 +374,7 @@ trait BotAPI
$data['document']['_'] = 'bot_'.$type_name;
$res['file_size'] = $data['document']['size'];
$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'] : ''];
default:

View File

@ -29,7 +29,7 @@ trait BotAPIFiles
$photoSize['location']['secret'] = $photo['location']['secret'] ?? 0;
$photoSize['location']['dc_id'] = $photo['dc_id'] ?? 0;
$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 [
'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)) {
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['_'])];
switch ($deserialized['_']) {
case 'bot_thumbnail':

View File

@ -22,69 +22,123 @@ namespace danog\MadelineProto\TL;
use danog\MadelineProto\MTProto;
/**
* TL serialization
* TL serialization.
*/
class TL
{
/**
* Highest available secret chat layer version
* Highest available secret chat layer version.
*
* @var integer
*/
public $encrypted_layer = -1;
private $encrypted_layer = -1;
/**
* Constructors
* Constructors.
*
* @var TLConstructor
*/
public $constructors;
private $constructors;
/**
* Methods
* Methods.
*
* @var TLMethods
* @var TLMethod
*/
public $methods;
private $methods;
/**
* TD Constructors
* TD Constructors.
*
* @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
*/
public $td_descriptions;
private $td_descriptions;
/**
* TL callbacks
* TL callbacks.
*
* @var array
*/
public $tl_callbacks = [];
private $tl_callbacks = [];
/**
* API instance
* API instance.
*
* @var \danog\MadelineProto\MTProto
*/
private $API;
/**
* Constructor function
* Constructor function.
*
* @param MTProto $API API instance
*/
public function __construct(MTProto $API) {
public function __construct($API = null)
{
$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->constructors = new TLConstructor();
$this->methods = new TLMethod();
@ -92,7 +146,7 @@ class TL
$this->td_methods = new TLMethod();
$this->td_descriptions = ['types' => [], 'constructors' => [], 'methods' => []];
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));
$TL_dict = \json_decode($filec, true);
if ($TL_dict === null) {
@ -165,7 +219,7 @@ class TL
if (\preg_match('/^[^\s]+#([a-f0-9]*)/i', $line, $matches)) {
$nid = \str_pad($matches[1], 8, '0', \STR_PAD_LEFT);
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;
}
@ -210,15 +264,14 @@ class TL
if (empty($TL_dict) || empty($TL_dict['constructors']) || !isset($TL_dict['methods'])) {
throw new Exception(\danog\MadelineProto\Lang::$current_lang['src_file_invalid'].$file);
}
$orig = $this->encrypted_layer;
$this->logger->logger(\danog\MadelineProto\Lang::$current_lang['translating_obj'], \danog\MadelineProto\Logger::ULTRA_VERBOSE);
$this->API->logger->logger(\danog\MadelineProto\Lang::$current_lang['translating_obj'], \danog\MadelineProto\Logger::ULTRA_VERBOSE);
foreach ($TL_dict['constructors'] as $elem) {
if ($scheme_type === 'secret') {
$this->encrypted_layer = \max($this->encrypted_layer, $elem['layer']);
}
$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) {
$this->{($scheme_type === 'td' ? 'td_' : '').'methods'}->add($elem);
if ($scheme_type === 'secret') {
@ -253,7 +306,12 @@ class TL
}
}
public function getMethodNamespaces()
/**
* Get TL namespaces.
*
* @return array
*/
public function getMethodNamespaces(): array
{
$res = [];
foreach ($this->methods->method_namespace as $pair) {
@ -264,16 +322,28 @@ class TL
return $res;
}
public function getMethodsNamespaced()
/**
* Get namespaced methods (method => namespace).
*
* @return array
*/
public function getMethodsNamespaced(): array
{
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 = [];
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!');
}
$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);
if ($tl_elem === false) {
@ -309,7 +385,17 @@ class TL
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']) {
case 'int':
@ -464,7 +550,7 @@ class TL
$predicate = $object['_'];
$constructorData = $this->constructors->findByPredicate($predicate, $layer);
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));
}
@ -486,7 +572,15 @@ class TL
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 ($matches[1] === '') {
@ -523,7 +617,7 @@ class TL
) &&
$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'])) {
$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)) {
$res = yield $this->getInfo($arguments['chat_id']);
$res = yield $this->API->getInfo($arguments['chat_id']);
if ($res['type'] !== 'chat') {
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);
}
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 = '';
$arguments = yield $this->botAPIToMTProto($arguments);
$arguments = yield $this->API->botAPIToMTProto($arguments);
$flags = 0;
foreach ($tl['params'] as $cur_flag) {
if (isset($cur_flag['pow'])) {
@ -595,7 +699,7 @@ class TL
foreach ($tl['params'] as $current_argument) {
if (!isset($arguments[$current_argument['name']])) {
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;
}
if ($current_argument['name'] === 'random_bytes') {
@ -603,7 +707,7 @@ class TL
continue;
}
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;
}
if ($current_argument['name'] === 'random_id') {
@ -691,27 +795,35 @@ class TL
)
&& $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 (\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 {
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']);
}
$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);
}
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)) {
$res = \fopen('php://memory', 'rw+b');
@ -726,8 +838,14 @@ class TL
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' => ''])
{

View File

@ -96,7 +96,14 @@ class TLConstructor
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])) {
$constructor = $this->by_id[$id];

View File

@ -826,7 +826,7 @@ trait Tools
*
* @param object $obj Object
* @param string $var Attribute name
*
*
* @return mixed
* @access public
*/

View File

@ -22,10 +22,10 @@ namespace danog\MadelineProto\Wrappers;
trait DialogHandler
{
/**
* Get dialog peers
* Get dialog peers.
*
* @param boolean $force Whether to refetch all dialogs ignoring cache
*
*
* @return \Generator<array<Peer>>
*/
public function getDialogs($force = true): \Generator
@ -47,10 +47,10 @@ trait DialogHandler
}
/**
* Get full info of all dialogs
* Get full info of all dialogs.
*
* @param boolean $force Whether to refetch all dialogs ignoring cache
*
*
* @return \Generator
*/
public function getFullDialogs($force = true): \Generator

View File

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

View File

@ -44,7 +44,7 @@ trait Login
yield $this->logout();
}
$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->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)) {
$callbacks []= $this->minDatabase;
}
$this->updateCallbacks($callbacks);
$this->TL->updateCallbacks($callbacks);
$this->startUpdateSystem();

View File

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