Refactor public API: full encapsulation and strict typing

This commit is contained in:
Daniil Gentili 2019-12-28 16:07:09 +01:00
parent 9899d34b11
commit 894470a147
Signed by: danog
GPG Key ID: 8C1BE3B34B230CA7
25 changed files with 1157 additions and 782 deletions

View File

@ -21,6 +21,7 @@ namespace danog\MadelineProto;
use Amp\Promise; use Amp\Promise;
use danog\MadelineProto\TL\TL; use danog\MadelineProto\TL\TL;
use danog\MadelineProto\TL\TLCallback;
use phpDocumentor\Reflection\DocBlockFactory; use phpDocumentor\Reflection\DocBlockFactory;
class AnnotationsBuilder class AnnotationsBuilder
@ -78,9 +79,11 @@ class AnnotationsBuilder
} }
/** /**
* Create file InternalDoc with all interfaces. * Create internalDoc.
*
* @return void
*/ */
private function createInternalClasses() private function createInternalClasses(): void
{ {
\danog\MadelineProto\Logger::log('Creating internal classes...', \danog\MadelineProto\Logger::NOTICE); \danog\MadelineProto\Logger::log('Creating internal classes...', \danog\MadelineProto\Logger::NOTICE);
$handle = \fopen($this->output, 'w'); $handle = \fopen($this->output, 'w');
@ -92,6 +95,13 @@ class AnnotationsBuilder
foreach ($methods as $method) { foreach ($methods as $method) {
$ignoreMethods[$method->getName()] = $method->getName(); $ignoreMethods[$method->getName()] = $method->getName();
} }
$class = new \ReflectionClass(TLCallback::class);
$methods = $class->getMethods(\ReflectionMethod::IS_STATIC | \ReflectionMethod::IS_PUBLIC);
foreach ($methods as $method) {
$ignoreMethods[$method->getName()] = $method->getName();
}
\fclose($handle); \fclose($handle);
$handle = \fopen($this->output, 'w'); $handle = \fopen($this->output, 'w');
@ -169,6 +179,9 @@ class AnnotationsBuilder
if (isset($ignoreMethods[$name])) { if (isset($ignoreMethods[$name])) {
continue; continue;
} }
if (\strpos($method->getDocComment() ?? '', '@internal') !== false) {
continue;
}
if ($name == 'methodCallAsyncRead') { if ($name == 'methodCallAsyncRead') {
$name = 'methodCall'; $name = 'methodCall';
@ -249,6 +262,13 @@ class AnnotationsBuilder
$doc .= " $ret \$this->__call(__FUNCTION__, $paramList);\n"; $doc .= " $ret \$this->__call(__FUNCTION__, $paramList);\n";
$doc .= "}\n"; $doc .= "}\n";
if (!$method->getDocComment()) {
Logger::log("$name has no PHPDOC!", Logger::FATAL_ERROR);
}
if (!$type) {
Logger::log("$name has no return type!", Logger::FATAL_ERROR);
}
$internalDoc['InternalDoc'][$name]['method'] = $method->getDocComment() ?? ''; $internalDoc['InternalDoc'][$name]['method'] = $method->getDocComment() ?? '';
$internalDoc['InternalDoc'][$name]['method'] .= "\n ".\implode("\n ", \explode("\n", $doc)); $internalDoc['InternalDoc'][$name]['method'] .= "\n ".\implode("\n ", \explode("\n", $doc));
} }

View File

@ -635,7 +635,6 @@ class Lang
public static function addToLang(string $key, string $value = '', bool $force = false) public static function addToLang(string $key, string $value = '', bool $force = false)
{ {
if (!isset(\danog\MadelineProto\Lang::$lang['en'][$key]) || $force) { if (!isset(\danog\MadelineProto\Lang::$lang['en'][$key]) || $force) {
\var_dump("Adding $key");
\danog\MadelineProto\Lang::$lang['en'][$key] = $value; \danog\MadelineProto\Lang::$lang['en'][$key] = $value;
\file_put_contents(__DIR__.'/Lang.php', \sprintf(self::$template, \var_export(\danog\MadelineProto\Lang::$lang, true), \var_export(\danog\MadelineProto\Lang::$lang['en'], true))); \file_put_contents(__DIR__.'/Lang.php', \sprintf(self::$template, \var_export(\danog\MadelineProto\Lang::$lang, true), \var_export(\danog\MadelineProto\Lang::$lang['en'], true)));
} }

File diff suppressed because it is too large Load Diff

View File

@ -257,10 +257,10 @@ class Logger
* *
* @return void * @return void
*/ */
public function logger($param, int $level = self::NOTICE, string $file = '') public function logger($param, int $level = self::NOTICE, string $file = ''): void
{ {
if ($level > $this->level || $this->mode === 0) { if ($level > $this->level || $this->mode === 0) {
return false; return;
} }
if (!self::$printed) { if (!self::$printed) {
self::$printed = true; self::$printed = true;
@ -274,7 +274,8 @@ class Logger
$this->colors[self::NOTICE] = \implode(';', [self::FOREGROUND['yellow'], self::SET['bold']]); $this->colors[self::NOTICE] = \implode(';', [self::FOREGROUND['yellow'], self::SET['bold']]);
} }
if ($this->mode === 4) { if ($this->mode === 4) {
return \call_user_func_array($this->optional, [$param, $level]); \call_user_func_array($this->optional, [$param, $level]);
return;
} }
$prefix = $this->prefix; $prefix = $this->prefix;
if (\danog\MadelineProto\Magic::$has_thread && \is_object(\Thread::getCurrentThread())) { if (\danog\MadelineProto\Magic::$has_thread && \is_object(\Thread::getCurrentThread())) {

View File

@ -581,9 +581,9 @@ class MTProto extends AsyncConstruct implements TLCallback
/** /**
* Cleanup memory and session file. * Cleanup memory and session file.
* *
* @return void * @return self
*/ */
public function cleanup() public function cleanup(): self
{ {
$this->referenceDatabase = new ReferenceDatabase($this); $this->referenceDatabase = new ReferenceDatabase($this);
$callbacks = [$this, $this->referenceDatabase]; $callbacks = [$this, $this->referenceDatabase];
@ -603,13 +603,13 @@ class MTProto extends AsyncConstruct implements TLCallback
* *
* @return void * @return void
*/ */
public function logger($param, int $level = Logger::NOTICE, string $file = '') public function logger($param, int $level = Logger::NOTICE, string $file = ''): void
{ {
if ($file === null) { if ($file === null) {
$file = \basename(\debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1)[0]['file'], '.php'); $file = \basename(\debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1)[0]['file'], '.php');
} }
return isset($this->logger) ? $this->logger->logger($param, $level, $file) : Logger::$default->logger($param, $level, $file); isset($this->logger) ? $this->logger->logger($param, $level, $file) : Logger::$default->logger($param, $level, $file);
} }
/** /**
@ -767,6 +767,8 @@ class MTProto extends AsyncConstruct implements TLCallback
/** /**
* Clean up properties from previous versions of MadelineProto. * Clean up properties from previous versions of MadelineProto.
* *
* @internal
*
* @return void * @return void
*/ */
private function cleanupProperties() private function cleanupProperties()
@ -1358,7 +1360,7 @@ class MTProto extends AsyncConstruct implements TLCallback
* *
* @return void * @return void
*/ */
public function parseSettings(array $settings) private function parseSettings(array $settings): void
{ {
$settings = self::getSettings($settings, $this->settings); $settings = self::getSettings($settings, $this->settings);
if ($settings['app_info'] === null) { if ($settings['app_info'] === null) {
@ -1377,7 +1379,7 @@ class MTProto extends AsyncConstruct implements TLCallback
* *
* @return void * @return void
*/ */
public function setupLogger() public function setupLogger(): void
{ {
$this->logger = Logger::getLoggerFromSettings($this->settings, isset($this->authorization['user']) ? isset($this->authorization['user']['username']) ? $this->authorization['user']['username'] : $this->authorization['user']['id'] : ''); $this->logger = Logger::getLoggerFromSettings($this->settings, isset($this->authorization['user']) ? isset($this->authorization['user']['username']) ? $this->authorization['user']['username'] : $this->authorization['user']['id'] : '');
} }
@ -1388,9 +1390,11 @@ class MTProto extends AsyncConstruct implements TLCallback
* @param boolean $de Whether to reset the session ID * @param boolean $de Whether to reset the session ID
* @param boolean $auth_key Whether to reset the auth key * @param boolean $auth_key Whether to reset the auth key
* *
* @internal
*
* @return void * @return void
*/ */
public function resetMTProtoSession(bool $de = true, bool $auth_key = false) public function resetMTProtoSession(bool $de = true, bool $auth_key = false): void
{ {
if (!\is_object($this->datacenter)) { if (!\is_object($this->datacenter)) {
throw new Exception(Lang::$current_lang['session_corrupted']); throw new Exception(Lang::$current_lang['session_corrupted']);
@ -1440,6 +1444,8 @@ class MTProto extends AsyncConstruct implements TLCallback
/** /**
* Whether we're initing authorization. * Whether we're initing authorization.
* *
* @internal
*
* @return boolean * @return boolean
*/ */
public function isInitingAuthorization() public function isInitingAuthorization()
@ -1493,7 +1499,7 @@ class MTProto extends AsyncConstruct implements TLCallback
* *
* @return void * @return void
*/ */
public function resetSession() public function resetSession(): void
{ {
if (isset($this->seqUpdater)) { if (isset($this->seqUpdater)) {
$this->seqUpdater->signal(true); $this->seqUpdater->signal(true);
@ -1539,7 +1545,7 @@ class MTProto extends AsyncConstruct implements TLCallback
* *
* @return void * @return void
*/ */
public function resetUpdateState() public function resetUpdateState(): void
{ {
if (isset($this->seqUpdater)) { if (isset($this->seqUpdater)) {
$this->seqUpdater->signal(true); $this->seqUpdater->signal(true);
@ -1571,9 +1577,11 @@ class MTProto extends AsyncConstruct implements TLCallback
* *
* @param boolean $anyway Force start update system? * @param boolean $anyway Force start update system?
* *
* @internal
*
* @return void * @return void
*/ */
public function startUpdateSystem($anyway = false) public function startUpdateSystem($anyway = false): void
{ {
if ($this->asyncInitPromise && !$anyway) { if ($this->asyncInitPromise && !$anyway) {
$this->logger("Not starting update system"); $this->logger("Not starting update system");
@ -1617,9 +1625,11 @@ class MTProto extends AsyncConstruct implements TLCallback
* *
* @param mixed $watcherId Watcher ID * @param mixed $watcherId Watcher ID
* *
* @return void * @internal
*
* @return \Generator<void>
*/ */
public function getPhoneConfig($watcherId = null) public function getPhoneConfig($watcherId = null): \Generator
{ {
if ($this->authorized === self::LOGGED_IN && \class_exists(VoIPServerConfigInternal::class) && !$this->authorization['user']['bot'] && $this->datacenter->getDataCenterConnection($this->settings['connection_settings']['default_dc'])->hasTempAuthKey()) { if ($this->authorized === self::LOGGED_IN && \class_exists(VoIPServerConfigInternal::class) && !$this->authorization['user']['bot'] && $this->datacenter->getDataCenterConnection($this->settings['connection_settings']['default_dc'])->hasTempAuthKey()) {
$this->logger->logger('Fetching phone config...'); $this->logger->logger('Fetching phone config...');
@ -1747,16 +1757,35 @@ class MTProto extends AsyncConstruct implements TLCallback
return $this->authorization['user']; return $this->authorization['user'];
} }
/**
* Called right before serialization of method starts.
*
* Pass the method name
*
* @return array
*/
public function getMethodCallbacks(): array public function getMethodCallbacks(): array
{ {
return []; return [];
} }
/**
* Called right before serialization of method starts.
*
* Pass the method name
*
* @return array
*/
public function getMethodBeforeCallbacks(): array public function getMethodBeforeCallbacks(): array
{ {
return []; return [];
} }
/**
* Called right after deserialization of object, passing the final object.
*
* @return array
*/
public function getConstructorCallbacks(): array public function getConstructorCallbacks(): array
{ {
return \array_merge( return \array_merge(
@ -1766,16 +1795,38 @@ class MTProto extends AsyncConstruct implements TLCallback
); );
} }
/**
* Called right before deserialization of object.
*
* Pass only the constructor name
*
* @return array
*/
public function getConstructorBeforeCallbacks(): array public function getConstructorBeforeCallbacks(): array
{ {
return []; return [];
} }
/**
* Called right before serialization of constructor.
*
* Passed the object, will return a modified version.
*
* @return array
*/
public function getConstructorSerializeCallbacks(): array public function getConstructorSerializeCallbacks(): array
{ {
return []; return [];
} }
/**
* Called if objects of the specified type cannot be serialized.
*
* Passed the unserializable object,
* will try to convert it to an object of the proper type.
*
* @return array
*/
public function getTypeMismatchCallbacks(): array public function getTypeMismatchCallbacks(): array
{ {
return \array_merge( return \array_merge(

View File

@ -56,7 +56,7 @@ trait Files
* @param callable $cb Callback (DEPRECATED, use FileCallbackInterface) * @param callable $cb Callback (DEPRECATED, use FileCallbackInterface)
* @param boolean $encrypted Whether to encrypt file for secret chats * @param boolean $encrypted Whether to encrypt file for secret chats
* *
* @return array * @return \Generator<array>
*/ */
public function upload($file, string $file_name = '', $cb = null, bool $encrypted = false): \Generator public function upload($file, string $file_name = '', $cb = null, bool $encrypted = false): \Generator
{ {
@ -229,9 +229,9 @@ trait Files
* @param boolean $seekable Whether chunks can be fetched out of order * @param boolean $seekable Whether chunks can be fetched out of order
* @param boolean $encrypted Whether to encrypt file for secret chats * @param boolean $encrypted Whether to encrypt file for secret chats
* *
* @return array * @return \Generator<array>
*/ */
public function uploadFromCallable($callable, int $size, string $mime, string $file_name = '', $cb = null, bool $seekable = true, bool $encrypted = false) public function uploadFromCallable($callable, int $size, string $mime, string $file_name = '', $cb = null, bool $seekable = true, bool $encrypted = false): \Generator
{ {
if (\is_object($callable) && $callable instanceof FileCallbackInterface) { if (\is_object($callable) && $callable instanceof FileCallbackInterface) {
$cb = $callable; $cb = $callable;
@ -360,9 +360,9 @@ trait Files
* @param string $file_name File name * @param string $file_name File name
* @param callable $cb Callback (DEPRECATED, use FileCallbackInterface) * @param callable $cb Callback (DEPRECATED, use FileCallbackInterface)
* *
* @return array * @return \Generator<array>
*/ */
public function uploadEncrypted($file, string $file_name = '', $cb = null) public function uploadEncrypted($file, string $file_name = '', $cb = null): \Generator
{ {
return $this->upload($file, $file_name, $cb, true); return $this->upload($file, $file_name, $cb, true);
} }
@ -374,9 +374,9 @@ trait Files
* @param callable $cb Callback (DEPRECATED, use FileCallbackInterface) * @param callable $cb Callback (DEPRECATED, use FileCallbackInterface)
* @param boolean $encrypted Whether to encrypt file for secret chats * @param boolean $encrypted Whether to encrypt file for secret chats
* *
* @return array * @return \Generator<array>
*/ */
public function uploadFromTgfile($media, $cb = null, bool $encrypted = false) public function uploadFromTgfile($media, $cb = null, bool $encrypted = false): \Generator
{ {
if (\is_object($media) && $media instanceof FileCallbackInterface) { if (\is_object($media) && $media instanceof FileCallbackInterface) {
$cb = $media; $cb = $media;
@ -447,7 +447,7 @@ trait Files
return $res; return $res;
} }
public function genAllFile($media) private function genAllFile($media)
{ {
$res = [$this->TL->getConstructors()->findByPredicate($media['_'])['type'] => $media]; $res = [$this->TL->getConstructors()->findByPredicate($media['_'])['type'] => $media];
switch ($media['_']) { switch ($media['_']) {
@ -540,7 +540,14 @@ trait Files
return $res; return $res;
} }
public function getFileInfo($constructor) /**
* Get info about file.
*
* @param mixed $constructor File ID
*
* @return \Generator<array>
*/
public function getFileInfo($constructor): \Generator
{ {
if (\is_string($constructor)) { if (\is_string($constructor)) {
$constructor = $this->unpackFileId($constructor)['MessageMedia']; $constructor = $this->unpackFileId($constructor)['MessageMedia'];
@ -932,6 +939,15 @@ trait Files
header('Content-Length: '.$info['size']); header('Content-Length: '.$info['size']);
header('Content-Type: '.$info['mime']); header('Content-Type: '.$info['mime']);
}*/ }*/
/**
* Extract photo size.
*
* @param mixed $photo Photo
*
* @internal
*
* @return void
*/
public function extractPhotosize($photo) public function extractPhotosize($photo)
{ {
} }
@ -1224,7 +1240,7 @@ trait Files
]; ];
} }
$x = 0; //$x = 0;
while (true) { while (true) {
try { try {
$res = yield $this->methodCallAsyncRead( $res = yield $this->methodCallAsyncRead(
@ -1241,7 +1257,7 @@ trait Files
break; break;
} catch (\danog\MadelineProto\RPCErrorException $e) { } catch (\danog\MadelineProto\RPCErrorException $e) {
if (\strpos($e->rpc, 'FLOOD_WAIT_') === 0) { if (\strpos($e->rpc, 'FLOOD_WAIT_') === 0) {
if ($x++ === 5) { /*if ($x++ === 5) {
if (isset($message_media['MessageMedia']) && !$this->authorization['user']['bot'] && $this->settings['download']['report_broken_media']) { if (isset($message_media['MessageMedia']) && !$this->authorization['user']['bot'] && $this->settings['download']['report_broken_media']) {
try { try {
yield $this->methodCallAsyncRead('messages.sendMedia', ['peer' => 'support', 'media' => $message_media['MessageMedia'], 'message' => "I can't download this file, could you please help?"], ['datacenter' => $this->datacenter->curdc]); yield $this->methodCallAsyncRead('messages.sendMedia', ['peer' => 'support', 'media' => $message_media['MessageMedia'], 'message' => "I can't download this file, could you please help?"], ['datacenter' => $this->datacenter->curdc]);
@ -1253,7 +1269,7 @@ trait Files
} }
throw new \danog\MadelineProto\Exception('The media server where this file is hosted is offline/overloaded, please try again later. Send the media to the telegram devs or to @danogentili to fix this.'); throw new \danog\MadelineProto\Exception('The media server where this file is hosted is offline/overloaded, please try again later. Send the media to the telegram devs or to @danogentili to fix this.');
} }*/
yield Tools::sleep(1); yield Tools::sleep(1);
continue; continue;
} }

View File

@ -51,7 +51,7 @@ trait PeerHandler
* *
* @return int * @return int
*/ */
public static function fromSupergroup($id) public static function fromSupergroup($id): int
{ {
return -$id - \pow(10, (int) \floor(\log(-$id, 10))); return -$id - \pow(10, (int) \floor(\log(-$id, 10)));
} }
@ -70,12 +70,28 @@ trait PeerHandler
return ($log - \intval($log)) * 1000 < 10; return ($log - \intval($log)) * 1000 < 10;
} }
public function addSupport($support) /**
* Set support info.
*
* @param array $support Support info
*
* @internal
*
* @return void
*/
public function addSupport(array $support): void
{ {
$this->supportUser = $support['user']['id']; $this->supportUser = $support['user']['id'];
} }
public function addUser($user) /**
* Add user info.
*
* @param array $user User info
*
* @return void
*/
public function addUser(array $user): void
{ {
if (!isset($user['access_hash']) && !($user['min'] ?? false)) { if (!isset($user['access_hash']) && !($user['min'] ?? false)) {
if (isset($this->chats[$user['id']]['access_hash']) && $this->chats[$user['id']]['access_hash']) { if (isset($this->chats[$user['id']]['access_hash']) && $this->chats[$user['id']]['access_hash']) {
@ -117,11 +133,19 @@ trait PeerHandler
break; break;
default: default:
throw new \danog\MadelineProto\Exception('Invalid user provided', $user); throw new \danog\MadelineProto\Exception('Invalid user provided', $user);
break;
} }
} }
public function addChat($chat) /**
* Add chat to database.
*
* @param array $chat Chat
*
* @internal
*
* @return void
*/
public function addChat($chat): \Generator
{ {
switch ($chat['_']) { switch ($chat['_']) {
case 'chat': case 'chat':
@ -182,7 +206,7 @@ trait PeerHandler
} }
} }
public function cachePwrChat($id, $full_fetch, $send) private function cachePwrChat($id, $full_fetch, $send)
{ {
\danog\MadelineProto\Tools::callFork((function () use ($id, $full_fetch, $send) { \danog\MadelineProto\Tools::callFork((function () use ($id, $full_fetch, $send) {
try { try {
@ -220,7 +244,16 @@ trait PeerHandler
} }
} }
public function entitiesPeerIsset($entities): \Generator /**
* Check if all peer entities are in db.
*
* @param array $entities Entity list
*
* @internal
*
* @return \Generator<bool>
*/
public function entitiesPeerIsset(array $entities): \Generator
{ {
try { try {
foreach ($entities as $entity) { foreach ($entities as $entity) {
@ -237,7 +270,16 @@ trait PeerHandler
return true; return true;
} }
public function fwdPeerIsset($fwd): \Generator /**
* Check if fwd peer is set.
*
* @param array $fwd Forward info
*
* @internal
*
* @return \Generator
*/
public function fwdPeerIsset(array $fwd): \Generator
{ {
try { try {
if (isset($fwd['user_id']) && !yield $this->peerIsset($fwd['user_id'])) { if (isset($fwd['user_id']) && !yield $this->peerIsset($fwd['user_id'])) {
@ -260,7 +302,7 @@ trait PeerHandler
* *
* @return ?int * @return ?int
*/ */
public function getFolderId($id) public function getFolderId($id): ?int
{ {
if (!\is_array($id)) { if (!\is_array($id)) {
return null; return null;
@ -593,7 +635,7 @@ trait PeerHandler
throw new \danog\MadelineProto\Exception('This peer is not present in the internal peer database'); throw new \danog\MadelineProto\Exception('This peer is not present in the internal peer database');
} }
public function genAll($constructor, $folder_id = null) private function genAll($constructor, $folder_id = null)
{ {
$res = [$this->TL->getConstructors()->findByPredicate($constructor['_'])['type'] => $constructor]; $res = [$this->TL->getConstructors()->findByPredicate($constructor['_'])['type'] => $constructor];
switch ($constructor['_']) { switch ($constructor['_']) {
@ -655,7 +697,14 @@ trait PeerHandler
return $res; return $res;
} }
public function fullChatLastUpdated($id) /**
* When were full info for this chat last cached.
*
* @param mixed $id Chat ID
*
* @return integer
*/
public function fullChatLastUpdated($id): int
{ {
return isset($this->full_chats[$id]['last_update']) ? $this->full_chats[$id]['last_update'] : 0; return isset($this->full_chats[$id]['last_update']) ? $this->full_chats[$id]['last_update'] : 0;
} }
@ -845,7 +894,7 @@ trait PeerHandler
return $res; return $res;
} }
public function recurseAlphabetSearchParticipants($channel, $filter, $q, $total_count, &$res) private function recurseAlphabetSearchParticipants($channel, $filter, $q, $total_count, &$res)
{ {
if (!yield $this->fetchParticipants($channel, $filter, $q, $total_count, $res)) { if (!yield $this->fetchParticipants($channel, $filter, $q, $total_count, $res)) {
return false; return false;
@ -856,7 +905,7 @@ trait PeerHandler
} }
} }
public function fetchParticipants($channel, $filter, $q, $total_count, &$res) private function fetchParticipants($channel, $filter, $q, $total_count, &$res)
{ {
$offset = 0; $offset = 0;
$limit = 200; $limit = 200;
@ -942,12 +991,12 @@ trait PeerHandler
return $has_more; return $has_more;
} }
public function fetchParticipantsCache($channel, $filter, $q, $offset, $limit) private function fetchParticipantsCache($channel, $filter, $q, $offset, $limit)
{ {
return $this->channel_participants[$channel['channel_id']][$filter][$q][$offset][$limit]; return $this->channel_participants[$channel['channel_id']][$filter][$q][$offset][$limit];
} }
public function storeParticipantsCache($gres, $channel, $filter, $q, $offset, $limit) private function storeParticipantsCache($gres, $channel, $filter, $q, $offset, $limit)
{ {
//return; //return;
unset($gres['users']); unset($gres['users']);
@ -960,12 +1009,12 @@ trait PeerHandler
$this->channel_participants[$channel['channel_id']][$filter][$q][$offset][$limit] = $gres; $this->channel_participants[$channel['channel_id']][$filter][$q][$offset][$limit] = $gres;
} }
public function getParticipantsHash($channel, $filter, $q, $offset, $limit) private function getParticipantsHash($channel, $filter, $q, $offset, $limit)
{ {
return isset($this->channel_participants[$channel['channel_id']][$filter][$q][$offset][$limit]) ? $this->channel_participants[$channel['channel_id']][$filter][$q][$offset][$limit]['hash'] : 0; return isset($this->channel_participants[$channel['channel_id']][$filter][$q][$offset][$limit]) ? $this->channel_participants[$channel['channel_id']][$filter][$q][$offset][$limit]['hash'] : 0;
} }
public function storeDb($res, $force = false) private function storeDb($res, $force = false)
{ {
$settings = isset($this->settings['connection_settings'][$this->datacenter->curdc]) ? $this->settings['connection_settings'][$this->datacenter->curdc] : $this->settings['connection_settings']['all']; $settings = isset($this->settings['connection_settings'][$this->datacenter->curdc]) ? $this->settings['connection_settings'][$this->datacenter->curdc] : $this->settings['connection_settings']['all'];
if (!isset($this->settings['pwr']) || $this->settings['pwr']['pwr'] === false || $settings['test_mode']) { if (!isset($this->settings['pwr']) || $this->settings['pwr']['pwr'] === false || $settings['test_mode']) {
@ -1007,7 +1056,14 @@ trait PeerHandler
} }
} }
public function resolveUsername($username) /**
* Resolve username (use getInfo instead).
*
* @param string $username Username
*
* @return \Generator
*/
public function resolveUsername(string $username): \Generator
{ {
try { try {
$this->caching_simple_username[$username] = true; $this->caching_simple_username[$username] = true;

View File

@ -35,6 +35,15 @@ trait UpdateHandler
public $updates = []; public $updates = [];
public $updates_key = 0; public $updates_key = 0;
/**
* PWR update handler.
*
* @param array $update Update
*
* @internal
*
* @return void
*/
public function pwrUpdateHandler($update) public function pwrUpdateHandler($update)
{ {
if (isset($this->settings['pwr']['updateHandler'])) { if (isset($this->settings['pwr']['updateHandler'])) {
@ -48,7 +57,16 @@ trait UpdateHandler
} }
} }
public function getUpdatesUpdateHandler($update) /**
* Getupdates update handler.
*
* @param array $update Update
*
* @internal
*
* @return void
*/
public function getUpdatesUpdateHandler(array $update): void
{ {
if (!$this->settings['updates']['handle_updates']) { if (!$this->settings['updates']['handle_updates']) {
return; return;
@ -56,7 +74,16 @@ trait UpdateHandler
$this->updates[$this->updates_key++] = $update; $this->updates[$this->updates_key++] = $update;
} }
public function getUpdates($params = []) /**
* Get updates.
*
* @param array $params Params
*
* @internal
*
* @return \Generator<array>
*/
public function getUpdates($params = []): \Generator
{ {
if (!$this->settings['updates']['handle_updates']) { if (!$this->settings['updates']['handle_updates']) {
$this->settings['updates']['handle_updates'] = true; $this->settings['updates']['handle_updates'] = true;
@ -98,7 +125,14 @@ trait UpdateHandler
public $update_resolved = false; public $update_resolved = false;
public $update_deferred; public $update_deferred;
public function waitUpdate() /**
* Wait for update.
*
* @internal
*
* @return \Generator
*/
public function waitUpdate(): \Generator
{ {
if (!$this->update_deferred) { if (!$this->update_deferred) {
$this->update_deferred = new Deferred(); $this->update_deferred = new Deferred();
@ -108,7 +142,14 @@ trait UpdateHandler
$this->update_deferred = new Deferred(); $this->update_deferred = new Deferred();
} }
public function signalUpdate() /**
* Signal update.
*
* @internal
*
* @return void
*/
public function signalUpdate(): void
{ {
if (!$this->update_deferred) { if (!$this->update_deferred) {
$this->update_deferred = new Deferred(); $this->update_deferred = new Deferred();
@ -121,7 +162,17 @@ trait UpdateHandler
}); });
} }
public function checkMsgId($message)
/**
* Check message ID.
*
* @param array $message Message
*
* @internal
*
* @return boolean
*/
public function checkMsgId(array $message): bool
{ {
if (!isset($message['to_id'])) { if (!isset($message['to_id'])) {
return true; return true;
@ -144,6 +195,13 @@ trait UpdateHandler
return false; return false;
} }
/**
* Get channel state.
*
* @internal
*
* @return UpdatesState|UpdatesState[]
*/
public function loadUpdateState() public function loadUpdateState()
{ {
if (!$this->got_state) { if (!$this->got_state) {
@ -154,17 +212,41 @@ trait UpdateHandler
return $this->channels_state->get(false); return $this->channels_state->get(false);
} }
/**
* Load channel state.
*
* @param ?int $channelId Channel ID
* @param array $init Init
*
* @internal
*
* @return UpdatesState|UpdatesState[]
*/
public function loadChannelState($channelId = null, $init = []) public function loadChannelState($channelId = null, $init = [])
{ {
return $this->channels_state->get($channelId, $init); return $this->channels_state->get($channelId, $init);
} }
/**
* Get channel states.
*
* @internal
*
* @return CombinedUpdatesState
*/
public function getChannelStates() public function getChannelStates()
{ {
return $this->channels_state; return $this->channels_state;
} }
public function getUpdatesState() /**
* Get update state.
*
* @internal
*
* @return \Generator
*/
public function getUpdatesState(): \Generator
{ {
$data = yield $this->methodCallAsyncRead('updates.getState', [], ['datacenter' => $this->settings['connection_settings']['default_dc']]); $data = yield $this->methodCallAsyncRead('updates.getState', [], ['datacenter' => $this->settings['connection_settings']['default_dc']]);
yield $this->getCdnConfig($this->settings['connection_settings']['default_dc']); yield $this->getCdnConfig($this->settings['connection_settings']['default_dc']);
@ -173,7 +255,17 @@ trait UpdateHandler
} }
public function handleUpdates($updates, $actual_updates = null) /**
* Undocumented function.
*
* @param array $updates Updates
* @param array $actual_updates Actual updates for deferred
*
* @internal
*
* @return \Generator
*/
public function handleUpdates($updates, $actual_updates = null): \Generator
{ {
if (!$this->settings['updates']['handle_updates']) { if (!$this->settings['updates']['handle_updates']) {
return; return;
@ -252,7 +344,16 @@ trait UpdateHandler
break; break;
} }
} }
public function saveUpdate($update) /**
* Save update.
*
* @param array $update Update to save
*
* @internal
*
* @return \Generator
*/
public function saveUpdate(array $update): \Generator
{ {
if ($update['_'] === 'updateConfig') { if ($update['_'] === 'updateConfig') {
$this->config['expires'] = 0; $this->config['expires'] = 0;
@ -388,14 +489,21 @@ trait UpdateHandler
} }
} }
public function pwrWebhook($update) /**
* Send update to webhook.
*
* @param array $update Update
*
* @return void
*/
private function pwrWebhook(array $update): void
{ {
$payload = \json_encode($update); $payload = \json_encode($update);
//$this->logger->logger($update, $payload, json_last_error()); //$this->logger->logger($update, $payload, json_last_error());
if ($payload === '') { if ($payload === '') {
$this->logger->logger('EMPTY UPDATE'); $this->logger->logger('EMPTY UPDATE');
return false; return;
} }
\danog\MadelineProto\Tools::callFork((function () use ($payload) { \danog\MadelineProto\Tools::callFork((function () use ($payload) {
$request = new Request($this->hook_url, 'POST'); $request = new Request($this->hook_url, 'POST');

View File

@ -24,7 +24,17 @@ namespace danog\MadelineProto\SecretChats;
*/ */
trait MessageHandler trait MessageHandler
{ {
public function encryptSecretMessage($chat_id, $message) /**
* Encrypt secret chat message.
*
* @param integer $chat_id Chat ID
* @param array $message Message to encrypt
*
* @internal
*
* @return \Generator
*/
public function encryptSecretMessage(int $chat_id, array $message): \Generator
{ {
if (!isset($this->secret_chats[$chat_id])) { if (!isset($this->secret_chats[$chat_id])) {
$this->logger->logger(\sprintf(\danog\MadelineProto\Lang::$current_lang['secret_chat_skipping'], $chat_id)); $this->logger->logger(\sprintf(\danog\MadelineProto\Lang::$current_lang['secret_chat_skipping'], $chat_id));
@ -61,7 +71,7 @@ trait MessageHandler
return $message; return $message;
} }
public function handleEncryptedUpdate($message, $test = false) private function handleEncryptedUpdate(array $message): \Generator
{ {
if (!isset($this->secret_chats[$message['message']['chat_id']])) { if (!isset($this->secret_chats[$message['message']['chat_id']])) {
$this->logger->logger(\sprintf(\danog\MadelineProto\Lang::$current_lang['secret_chat_skipping'], $message['message']['chat_id'])); $this->logger->logger(\sprintf(\danog\MadelineProto\Lang::$current_lang['secret_chat_skipping'], $message['message']['chat_id']));
@ -122,7 +132,7 @@ trait MessageHandler
yield $this->handleDecryptedUpdate($message); yield $this->handleDecryptedUpdate($message);
} }
public function tryMTProtoV1Decrypt($message_key, $chat_id, $old, $encrypted_data) private function tryMTProtoV1Decrypt($message_key, $chat_id, $old, $encrypted_data)
{ {
list($aes_key, $aes_iv) = $this->oldAesCalculate($message_key, $this->secret_chats[$chat_id][$old ? 'old_key' : 'key']['auth_key'], true); list($aes_key, $aes_iv) = $this->oldAesCalculate($message_key, $this->secret_chats[$chat_id][$old ? 'old_key' : 'key']['auth_key'], true);
$decrypted_data = $this->igeDecrypt($encrypted_data, $aes_key, $aes_iv); $decrypted_data = $this->igeDecrypt($encrypted_data, $aes_key, $aes_iv);
@ -144,7 +154,7 @@ trait MessageHandler
return $message_data; return $message_data;
} }
public function tryMTProtoV2Decrypt($message_key, $chat_id, $old, $encrypted_data) private function tryMTProtoV2Decrypt($message_key, $chat_id, $old, $encrypted_data)
{ {
list($aes_key, $aes_iv) = $this->aesCalculate($message_key, $this->secret_chats[$chat_id][$old ? 'old_key' : 'key']['auth_key'], !$this->secret_chats[$chat_id]['admin']); list($aes_key, $aes_iv) = $this->aesCalculate($message_key, $this->secret_chats[$chat_id][$old ? 'old_key' : 'key']['auth_key'], !$this->secret_chats[$chat_id]['admin']);
$decrypted_data = $this->igeDecrypt($encrypted_data, $aes_key, $aes_iv); $decrypted_data = $this->igeDecrypt($encrypted_data, $aes_key, $aes_iv);

View File

@ -24,7 +24,7 @@ namespace danog\MadelineProto\SecretChats;
*/ */
trait ResponseHandler trait ResponseHandler
{ {
public function handleDecryptedUpdate($update) private function handleDecryptedUpdate($update)
{ {
/*if (isset($update['message']['decrypted_message']['random_bytes']) && strlen($update['message']['decrypted_message']['random_bytes']) < 15) { /*if (isset($update['message']['decrypted_message']['random_bytes']) && strlen($update['message']['decrypted_message']['random_bytes']) < 15) {
throw new \danog\MadelineProto\ResponseException(\danog\MadelineProto\Lang::$current_lang['rand_bytes_too_short']); throw new \danog\MadelineProto\ResponseException(\danog\MadelineProto\Lang::$current_lang['rand_bytes_too_short']);

View File

@ -24,7 +24,7 @@ namespace danog\MadelineProto\SecretChats;
*/ */
trait SeqNoHandler trait SeqNoHandler
{ {
public function checkSecretInSeqNo($chat_id, $seqno) private function checkSecretInSeqNo($chat_id, $seqno)
{ {
$seqno = ($seqno - $this->secret_chats[$chat_id]['out_seq_no_x']) / 2; $seqno = ($seqno - $this->secret_chats[$chat_id]['out_seq_no_x']) / 2;
$last = 0; $last = 0;
@ -47,7 +47,7 @@ trait SeqNoHandler
return true; return true;
} }
public function checkSecretOutSeqNo($chat_id, $seqno) private function checkSecretOutSeqNo($chat_id, $seqno)
{ {
$seqno = ($seqno - $this->secret_chats[$chat_id]['in_seq_no_x']) / 2; $seqno = ($seqno - $this->secret_chats[$chat_id]['in_seq_no_x']) / 2;
$C = 0; $C = 0;
@ -78,12 +78,12 @@ trait SeqNoHandler
return true; return true;
} }
public function generateSecretInSeqNo($chat) private function generateSecretInSeqNo($chat)
{ {
return $this->secret_chats[$chat]['layer'] > 8 ? $this->secret_chats[$chat]['in_seq_no'] * 2 + $this->secret_chats[$chat]['in_seq_no_x'] : -1; return $this->secret_chats[$chat]['layer'] > 8 ? $this->secret_chats[$chat]['in_seq_no'] * 2 + $this->secret_chats[$chat]['in_seq_no_x'] : -1;
} }
public function generateSecretOutSeqNo($chat) private function generateSecretOutSeqNo($chat)
{ {
return $this->secret_chats[$chat]['layer'] > 8 ? $this->secret_chats[$chat]['out_seq_no'] * 2 + $this->secret_chats[$chat]['out_seq_no_x'] : -1; return $this->secret_chats[$chat]['layer'] > 8 ? $this->secret_chats[$chat]['out_seq_no'] * 2 + $this->secret_chats[$chat]['out_seq_no_x'] : -1;
} }

View File

@ -23,12 +23,19 @@ use danog\MadelineProto\Logger;
trait BotAPI trait BotAPI
{ {
public function htmlEntityDecode($stuff) private function htmlEntityDecode($stuff)
{ {
return \html_entity_decode(\preg_replace('#< *br */? *>#', "\n", $stuff)); return \html_entity_decode(\preg_replace('#< *br */? *>#', "\n", $stuff));
} }
public function mbStrlen($text) /**
* Get Telegram UTF-8 length of string.
*
* @param string $text Text
*
* @return int
*/
public function mbStrlen(string $text): int
{ {
$length = 0; $length = 0;
$textlength = \strlen($text); $textlength = \strlen($text);
@ -42,7 +49,16 @@ trait BotAPI
return $length; return $length;
} }
public function mbSubstr($text, $offset, $length = null) /**
* Telegram UTF-8 multibyte substring.
*
* @param string $text Text to substring
* @param integer $offset Offset
* @param ?int $length Length
*
* @return string
*/
public function mbSubstr(string $text, int $offset, $length = null): string
{ {
$mb_text_length = $this->mbStrlen($text); $mb_text_length = $this->mbStrlen($text);
if ($offset < 0) { if ($offset < 0) {
@ -75,7 +91,15 @@ trait BotAPI
return $new_text; return $new_text;
} }
public function mbStrSplit($text, $length) /**
* Telegram UTF-8 multibyte split.
*
* @param string $text Text
* @param integer $length Length
*
* @return string
*/
public function mbStrSplit(string $text, int $length): string
{ {
$tlength = \mb_strlen($text, 'UTF-8'); $tlength = \mb_strlen($text, 'UTF-8');
$result = []; $result = [];
@ -86,7 +110,7 @@ trait BotAPI
return $result; return $result;
} }
public function parseButtons($rows) private function parseButtons($rows)
{ {
$newrows = []; $newrows = [];
$key = 0; $key = 0;
@ -125,7 +149,7 @@ trait BotAPI
return $newrows; return $newrows;
} }
public function parseReplyMarkup($markup) private function parseReplyMarkup($markup)
{ {
if (isset($markup['force_reply']) && $markup['force_reply']) { if (isset($markup['force_reply']) && $markup['force_reply']) {
$markup['_'] = 'replyKeyboardForceReply'; $markup['_'] = 'replyKeyboardForceReply';
@ -157,7 +181,15 @@ trait BotAPI
return $markup; return $markup;
} }
public function MTProtoToBotAPI($data, $sent_arguments = []) /**
* Convert MTProto parameters to bot API parameters.
*
* @param array $data Data
* @param array $sent_arguments Sent arguments
*
* @return \Generator<array>
*/
public function MTProtoToBotAPI(array $data, array $sent_arguments = []): \Generator
{ {
$newd = []; $newd = [];
if (!isset($data['_'])) { if (!isset($data['_'])) {
@ -382,7 +414,14 @@ trait BotAPI
} }
} }
public function botAPIToMTProto($arguments) /**
* Convert bot API parameters to MTProto parameters.
*
* @param array $arguments Arguments
*
* @return \Generator<array>
*/
public function botAPIToMTProto(array $arguments): \Generator
{ {
foreach (self::BOTAPI_PARAMS_CONVERSION as $bot => $mtproto) { foreach (self::BOTAPI_PARAMS_CONVERSION as $bot => $mtproto) {
if (isset($arguments[$bot]) && !isset($arguments[$mtproto])) { if (isset($arguments[$bot]) && !isset($arguments[$mtproto])) {
@ -400,7 +439,7 @@ trait BotAPI
return $arguments; return $arguments;
} }
public function parseNode($node, &$entities, &$new_message, &$offset) private function parseNode($node, &$entities, &$new_message, &$offset)
{ {
switch ($node->nodeName) { switch ($node->nodeName) {
case 'br': case 'br':
@ -518,7 +557,7 @@ trait BotAPI
} }
} }
public function parseMode($arguments) private function parseMode($arguments)
{ {
if ($arguments['message'] === '' || !isset($arguments['message']) || !isset($arguments['parse_mode'])) { if ($arguments['message'] === '' || !isset($arguments['message']) || !isset($arguments['parse_mode'])) {
return $arguments; return $arguments;
@ -555,7 +594,7 @@ trait BotAPI
return $arguments; return $arguments;
} }
public function splitToChunks($args) private function splitToChunks($args)
{ {
$args = yield $this->parseMode($args); $args = yield $this->parseMode($args);
if (!isset($args['entities'])) { if (!isset($args['entities'])) {
@ -665,7 +704,7 @@ trait BotAPI
return $multiple_args; return $multiple_args;
} }
public function multipleExplodeKeepDelimiters($delimiters, $string) private function multipleExplodeKeepDelimiters($delimiters, $string)
{ {
$initialArray = \explode(\chr(1), \str_replace($delimiters, \chr(1), $string)); $initialArray = \explode(\chr(1), \str_replace($delimiters, \chr(1), $string));
$finalArray = []; $finalArray = [];
@ -681,7 +720,7 @@ trait BotAPI
return $finalArray; return $finalArray;
} }
public function htmlFixtags($text) private function htmlFixtags($text)
{ {
$diff = 0; $diff = 0;
\preg_match_all('#(.*?)(<(\bu\b|\bs\b|\ba\b|\bb\b|\bstrong\b|\bblockquote\b|\bstrike\b|\bdel\b|\bem\b|i|\bcode\b|\bpre\b)[^>]*>)(.*?)([<]\s*/\s*\3[>])#is', $text, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE); \preg_match_all('#(.*?)(<(\bu\b|\bs\b|\ba\b|\bb\b|\bstrong\b|\bblockquote\b|\bstrike\b|\bdel\b|\bem\b|i|\bcode\b|\bpre\b)[^>]*>)(.*?)([<]\s*/\s*\3[>])#is', $text, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE);
@ -724,7 +763,7 @@ trait BotAPI
return \htmlentities($text); return \htmlentities($text);
} }
public function buildRows($button_list) private function buildRows($button_list)
{ {
$end = false; $end = false;
$rows = []; $rows = [];

View File

@ -21,7 +21,7 @@ namespace danog\MadelineProto\TL\Conversion;
trait BotAPIFiles trait BotAPIFiles
{ {
public function photosizeToBotAPI($photoSize, $photo, $thumbnail = false) private function photosizeToBotAPI($photoSize, $photo, $thumbnail = false)
{ {
$ext = '.jpg';//$this->getExtensionFromLocation(['_' => 'inputFileLocation', 'volume_id' => $photoSize['location']['volume_id'], 'local_id' => $photoSize['location']['local_id'], 'secret' => $photoSize['location']['secret'], 'dc_id' => $photoSize['location']['dc_id']], '.jpg'); $ext = '.jpg';//$this->getExtensionFromLocation(['_' => 'inputFileLocation', 'volume_id' => $photoSize['location']['volume_id'], 'local_id' => $photoSize['location']['local_id'], 'secret' => $photoSize['location']['secret'], 'dc_id' => $photoSize['location']['dc_id']], '.jpg');
$photoSize['location']['access_hash'] = $photo['access_hash'] ?? 0; $photoSize['location']['access_hash'] = $photo['access_hash'] ?? 0;
@ -41,7 +41,14 @@ trait BotAPIFiles
]; ];
} }
public function unpackFileId($file_id) /**
* Unpack bot API file ID.
*
* @param string $file_id Bot API file ID
*
* @return array Unpacked file ID
*/
public function unpackFileId(string $file_id): array
{ {
$file_id = \danog\MadelineProto\Tools::rleDecode(\danog\MadelineProto\Tools::base64urlDecode($file_id)); $file_id = \danog\MadelineProto\Tools::rleDecode(\danog\MadelineProto\Tools::base64urlDecode($file_id));
if ($file_id[\strlen($file_id) - 1] !== \chr(2)) { if ($file_id[\strlen($file_id) - 1] !== \chr(2)) {

View File

@ -14,22 +14,39 @@ If not, see <http://www.gnu.org/licenses/>.
namespace danog\MadelineProto\TL\Conversion; namespace danog\MadelineProto\TL\Conversion;
use danog\MadelineProto\MTProto;
/** /**
* Manages generation of extensions for files. * Manages generation of extensions for files.
*/ */
trait Extension trait Extension
{ {
public function getMimeFromExtension($extension, $default) /**
* Get mime type from file extension.
*
* @param string $extension File extension
* @param string $default Default mime type
*
* @return string
*/
public function getMimeFromExtension(string $extension, string $default): string
{ {
$ext = \ltrim($extension, '.'); $ext = \ltrim($extension, '.');
if (isset(self::ALL_MIMES[$ext])) { if (isset(MTProto::ALL_MIMES[$ext])) {
return self::ALL_MIMES[$ext][0]; return MTProto::ALL_MIMES[$ext][0];
} }
return $default; return $default;
} }
public function getExtensionFromMime($mime) /**
* Get extension from mime type.
*
* @param string $mime MIME type
*
* @return string
*/
public function getExtensionFromMime(string $mime): string
{ {
foreach (self::ALL_MIMES as $key => $value) { foreach (self::ALL_MIMES as $key => $value) {
if (\array_search($mime, $value) !== false) { if (\array_search($mime, $value) !== false) {
@ -40,7 +57,15 @@ trait Extension
return ''; return '';
} }
public function getExtensionFromLocation($location, $default) /**
* Get extension from file location.
*
* @param mixed $location File location
* @param string $default Default extension
*
* @return string
*/
public function getExtensionFromLocation($location, string $default): string
{ {
return $default; return $default;
//('upload.getFile', ['location' => $location, 'offset' => 0, 'limit' => 2], ['heavy' => true, 'datacenter' => $location['dc_id']]); //('upload.getFile', ['location' => $location, 'offset' => 0, 'limit' => 2], ['heavy' => true, 'datacenter' => $location['dc_id']]);
@ -69,14 +94,28 @@ trait Extension
} }
} }
public function getMimeFromFile($file) /**
* Get mime type of file.
*
* @param string $file File
*
* @return string
*/
public function getMimeFromFile(string $file): string
{ {
$finfo = new \finfo(FILEINFO_MIME_TYPE); $finfo = new \finfo(FILEINFO_MIME_TYPE);
return $finfo->file($file); return $finfo->file($file);
} }
public function getMimeFromBuffer($buffer) /**
* Get mime type from buffer.
*
* @param string $buffer Buffer
*
* @return string
*/
public function getMimeFromBuffer(string $buffer): string
{ {
$finfo = new \finfo(FILEINFO_MIME_TYPE); $finfo = new \finfo(FILEINFO_MIME_TYPE);

View File

@ -21,7 +21,15 @@ namespace danog\MadelineProto\TL\Conversion;
trait TD trait TD
{ {
public function tdcliToTd(&$params, $key = null) /**
* Convert tdcli parameters to tdcli.
*
* @param array $params Params
* @param array $key Key
*
* @return array
*/
public function tdcliToTd(&$params, $key = null): array
{ {
if (!\is_array($params)) { if (!\is_array($params)) {
return $params; return $params;
@ -44,7 +52,14 @@ trait TD
return $params; return $params;
} }
public function tdToMTProto($params) /**
* Convert TD to MTProto parameters.
*
* @param array $params Parameters
*
* @return \Generator<array>
*/
public function tdToMTProto(array $params): \Generator
{ {
$newparams = ['_' => self::REVERSE[$params['_']]]; $newparams = ['_' => self::REVERSE[$params['_']]];
foreach (self::TD_PARAMS_CONVERSION[$newparams['_']] as $td => $mtproto) { foreach (self::TD_PARAMS_CONVERSION[$newparams['_']] as $td => $mtproto) {
@ -75,12 +90,26 @@ trait TD
return $newparams; return $newparams;
} }
public function MTProtoToTdcli($params) /**
* MTProto to TDCLI params.
*
* @param mixed $params Params
*
* @return \Generator
*/
public function MTProtoToTdcli($params): \Generator
{ {
return $this->tdToTdcli(yield $this->MTProtoToTd($params)); return $this->tdToTdcli(yield $this->MTProtoToTd($params));
} }
public function MTProtoToTd(&$params) /**
* MTProto to TD params.
*
* @param mixed $params Params
*
* @return \Generator
*/
public function MTProtoToTd(&$params): \Generator
{ {
if (!\is_array($params)) { if (!\is_array($params)) {
return $params; return $params;
@ -163,6 +192,13 @@ trait TD
return $newparams; return $newparams;
} }
/**
* Convert TD parameters to tdcli.
*
* @param mixed $params Parameters
*
* @return mixed
*/
public function tdToTdcli($params) public function tdToTdcli($params)
{ {
if (!\is_array($params)) { if (!\is_array($params)) {

View File

@ -45,7 +45,7 @@ interface TLCallback
* *
* Pass the method name and arguments * Pass the method name and arguments
* *
* @var array * @return array
*/ */
public function getMethodCallbacks(): array; public function getMethodCallbacks(): array;
@ -54,14 +54,14 @@ interface TLCallback
* *
* Pass the method name * Pass the method name
* *
* @var array * @return array
*/ */
public function getMethodBeforeCallbacks(): array; public function getMethodBeforeCallbacks(): array;
/** /**
* Called right after deserialization of object, passing the final object. * Called right after deserialization of object, passing the final object.
* *
* @var array * @return array
*/ */
public function getConstructorCallbacks(): array; public function getConstructorCallbacks(): array;
@ -70,7 +70,7 @@ interface TLCallback
* *
* Pass only the constructor name * Pass only the constructor name
* *
* @var array * @return array
*/ */
public function getConstructorBeforeCallbacks(): array; public function getConstructorBeforeCallbacks(): array;
@ -79,7 +79,7 @@ interface TLCallback
* *
* Passed the object, will return a modified version. * Passed the object, will return a modified version.
* *
* @var array * @return array
*/ */
public function getConstructorSerializeCallbacks(): array; public function getConstructorSerializeCallbacks(): array;
@ -89,7 +89,7 @@ interface TLCallback
* Passed the unserializable object, * Passed the unserializable object,
* will try to convert it to an object of the proper type. * will try to convert it to an object of the proper type.
* *
* @var array * @return array
*/ */
public function getTypeMismatchCallbacks(): array; public function getTypeMismatchCallbacks(): array;
} }

View File

@ -480,7 +480,7 @@ trait Tools
* *
* @return void * @return void
*/ */
public static function callForkDefer($promise) public static function callForkDefer($promise): void
{ {
Loop::defer([__CLASS__, 'callFork'], $promise); Loop::defer([__CLASS__, 'callFork'], $promise);
} }
@ -493,7 +493,7 @@ trait Tools
* *
* @return void * @return void
*/ */
public static function rethrow(\Throwable $e, $file = '') public static function rethrow(\Throwable $e, $file = ''): void
{ {
$zis = isset($this) ? $this : null; $zis = isset($this) ? $this : null;
$logger = isset($zis->logger) ? $zis->logger : Logger::$default; $logger = isset($zis->logger) ? $zis->logger : Logger::$default;
@ -593,6 +593,8 @@ trait Tools
* @param integer $operation Locking mode * @param integer $operation Locking mode
* @param float $polling Polling interval * @param float $polling Polling interval
* *
* @internal Generator function
*
* @return \Generator * @return \Generator
*/ */
public static function flockGenerator(string $file, int $operation, float $polling): \Generator public static function flockGenerator(string $file, int $operation, float $polling): \Generator
@ -646,6 +648,8 @@ trait Tools
* *
* @param string $prompt Prompt * @param string $prompt Prompt
* *
* @internal Generator function
*
* @return \Generator * @return \Generator
*/ */
public static function readLineGenerator(string $prompt = ''): \Generator public static function readLineGenerator(string $prompt = ''): \Generator

View File

@ -29,22 +29,57 @@ trait AuthKeyHandler
{ {
private $calls = []; private $calls = [];
/**
* Request call (synchronous).
*
* @param mixed $user User info
*
* @internal
*
* @return \danog\MadelineProto\VoIPController
*/
public function requestCall($user) public function requestCall($user)
{ {
return \danog\MadelineProto\Tools::wait($this->requestCallAsync($user)); return \danog\MadelineProto\Tools::wait($this->requestCallAsync($user));
} }
public function acceptCall($user) /**
* Accept call (synchronous).
*
* @param mixed $user Accept call
*
* @internal
*
* @return bool
*/
public function acceptCall($user): bool
{ {
return \danog\MadelineProto\Tools::wait($this->acceptCallAsync($user)); return \danog\MadelineProto\Tools::wait($this->acceptCallAsync($user));
} }
public function discardCall($call, $reason, $rating = [], $need_debug = true) /**
* Discard call (synchronous).
*
* @param array $call Call
* @param string $reason Discard reason
* @param array $rating Rating
* @param boolean $need_debug Need debug?
*
* @return array
*/
public function discardCall($call, $reason, $rating = [], $need_debug = true): void
{ {
return \danog\MadelineProto\Tools::wait($this->discardCallAsync($call, $reason, $rating, $need_debug)); \danog\MadelineProto\Tools::wait($this->discardCallAsync($call, $reason, $rating, $need_debug));
} }
public function requestCallAsync($user) /**
* Request VoIP call.
*
* @param mixed $user User
*
* @return void
*/
public function requestCallAsync($user): \Generator
{ {
if (!\class_exists('\\danog\\MadelineProto\\VoIP')) { if (!\class_exists('\\danog\\MadelineProto\\VoIP')) {
throw \danog\MadelineProto\Exception::extension('libtgvoip'); throw \danog\MadelineProto\Exception::extension('libtgvoip');
@ -71,7 +106,14 @@ trait AuthKeyHandler
return $controller; return $controller;
} }
public function acceptCallAsync($call) /**
* Accept call.
*
* @param array $call Call
*
* @return \Generator
*/
public function acceptCallAsync($call): \Generator
{ {
if (!\class_exists('\\danog\\MadelineProto\\VoIP')) { if (!\class_exists('\\danog\\MadelineProto\\VoIP')) {
throw new \danog\MadelineProto\Exception(); throw new \danog\MadelineProto\Exception();
@ -111,7 +153,14 @@ trait AuthKeyHandler
return true; return true;
} }
public function confirmCall($params) /**
* Confirm call.
*
* @param array $params Params
*
* @return \Generator
*/
public function confirmCall($params): \Generator
{ {
if (!\class_exists('\\danog\\MadelineProto\\VoIP')) { if (!\class_exists('\\danog\\MadelineProto\\VoIP')) {
throw \danog\MadelineProto\Exception::extension('libtgvoip'); throw \danog\MadelineProto\Exception::extension('libtgvoip');
@ -160,7 +209,14 @@ trait AuthKeyHandler
return $res; return $res;
} }
public function completeCall($params) /**
* Complete call handshake.
*
* @param array $params Params
*
* @return \Generator
*/
public function completeCall($params): \Generator
{ {
if (!\class_exists('\\danog\\MadelineProto\\VoIP')) { if (!\class_exists('\\danog\\MadelineProto\\VoIP')) {
throw \danog\MadelineProto\Exception::extension('libtgvoip'); throw \danog\MadelineProto\Exception::extension('libtgvoip');
@ -195,7 +251,14 @@ trait AuthKeyHandler
return $this->calls[$params['id']]->startTheMagic(); return $this->calls[$params['id']]->startTheMagic();
} }
public function callStatus($id) /**
* Get call status.
*
* @param array $id Call ID
*
* @return integer
*/
public function callStatus($id): int
{ {
if (!\class_exists('\\danog\\MadelineProto\\VoIP')) { if (!\class_exists('\\danog\\MadelineProto\\VoIP')) {
throw \danog\MadelineProto\Exception::extension('libtgvoip'); throw \danog\MadelineProto\Exception::extension('libtgvoip');
@ -207,7 +270,14 @@ trait AuthKeyHandler
return \danog\MadelineProto\VoIP::CALL_STATE_NONE; return \danog\MadelineProto\VoIP::CALL_STATE_NONE;
} }
public function getCall($call) /**
* Get call info.
*
* @param mixed $call Call ID
*
* @return array
*/
public function getCall($call): array
{ {
if (!\class_exists('\\danog\\MadelineProto\\VoIP')) { if (!\class_exists('\\danog\\MadelineProto\\VoIP')) {
throw \danog\MadelineProto\Exception::extension('libtgvoip'); throw \danog\MadelineProto\Exception::extension('libtgvoip');
@ -216,7 +286,17 @@ trait AuthKeyHandler
return $this->calls[$call]; return $this->calls[$call];
} }
public function discardCallAsync($call, $reason, $rating = [], $need_debug = true) /**
* Discard call.
*
* @param array $call Call
* @param string $reason Discard reason
* @param array $rating Rating
* @param boolean $need_debug Need debug?
*
* @return \Generator
*/
public function discardCallAsync($call, $reason, $rating = [], $need_debug = true): \Generator
{ {
if (!\class_exists('\\danog\\MadelineProto\\VoIP')) { if (!\class_exists('\\danog\\MadelineProto\\VoIP')) {
throw \danog\MadelineProto\Exception::extension('libtgvoip'); throw \danog\MadelineProto\Exception::extension('libtgvoip');
@ -249,7 +329,12 @@ trait AuthKeyHandler
} }
unset($this->calls[$call['id']]); unset($this->calls[$call['id']]);
} }
public function checkCalls() /**
* Check state of calls.
*
* @return void
*/
public function checkCalls(): void
{ {
\array_walk($this->calls, function ($controller, $id) { \array_walk($this->calls, function ($controller, $id) {
if ($controller->getCallState() === \danog\MadelineProto\VoIP::CALL_STATE_ENDED) { if ($controller->getCallState() === \danog\MadelineProto\VoIP::CALL_STATE_ENDED) {

View File

@ -108,7 +108,7 @@ Note that you can also provide the API parameters directly in the code using the
exit; exit;
} }
public function webAPIPhoneLogin($settings) private function webAPIPhoneLogin($settings)
{ {
try { try {
$this->my_telegram_org_wrapper = new \danog\MadelineProto\MyTelegramOrgWrapper($settings); $this->my_telegram_org_wrapper = new \danog\MadelineProto\MyTelegramOrgWrapper($settings);
@ -119,7 +119,7 @@ Note that you can also provide the API parameters directly in the code using the
} }
} }
public function webAPICompleteLogin() private function webAPICompleteLogin()
{ {
try { try {
yield $this->my_telegram_org_wrapper->completeLogin($_POST['code']); yield $this->my_telegram_org_wrapper->completeLogin($_POST['code']);
@ -130,7 +130,7 @@ Note that you can also provide the API parameters directly in the code using the
} }
} }
public function webAPICreateApp() private function webAPICreateApp()
{ {
try { try {
$params = $_POST; $params = $_POST;

View File

@ -38,22 +38,32 @@ trait ApiTemplates
</body> </body>
</html>'; </html>';
public function webAPIEchoTemplate($message, $form) private function webAPIEchoTemplate($message, $form)
{ {
return \sprintf($this->web_api_template, $message, $form); return \sprintf($this->web_api_template, $message, $form);
} }
public function getWebAPITemplate() /**
* Get web API login HTML template string.
*
* @return string
*/
public function getWebAPITemplate(): string
{ {
return $this->web_template; return $this->web_template;
} }
public function setWebAPITemplate($template) /**
* Set web API login HTML template string.
*
* @return string
*/
public function setWebAPITemplate(string $template)
{ {
$this->web_template = $template; $this->web_template = $template;
} }
public function webAPIEcho($message = '') private function webAPIEcho(string $message = '')
{ {
$stdout = getOutputBufferStream(); $stdout = getOutputBufferStream();
if (!isset($this->my_telegram_org_wrapper)) { if (!isset($this->my_telegram_org_wrapper)) {

View File

@ -52,7 +52,7 @@ trait Events
* *
* @return void * @return void
*/ */
public function setEventHandler($event_handler) public function setEventHandler($event_handler): void
{ {
if (!\class_exists($event_handler) || !\is_subclass_of($event_handler, '\danog\MadelineProto\EventHandler')) { if (!\class_exists($event_handler) || !\is_subclass_of($event_handler, '\danog\MadelineProto\EventHandler')) {
throw new \danog\MadelineProto\Exception('Wrong event handler was defined'); throw new \danog\MadelineProto\Exception('Wrong event handler was defined');

View File

@ -27,7 +27,13 @@ use danog\MadelineProto\MTProtoTools\PasswordCalculator;
*/ */
trait Login trait Login
{ {
public function logout()
/**
* Log out currently logged in user.
*
* @return \Generator
*/
public function logout(): \Generator
{ {
yield $this->methodCallAsyncRead('auth.logOut', [], ['datacenter' => $this->datacenter->curdc]); yield $this->methodCallAsyncRead('auth.logOut', [], ['datacenter' => $this->datacenter->curdc]);
$this->resetSession(); $this->resetSession();
@ -37,7 +43,14 @@ trait Login
return true; return true;
} }
public function botLogin($token) /**
* Login as bot.
*
* @param string $token Bot token
*
* @return \Generator
*/
public function botLogin(string $token): \Generator
{ {
if ($this->authorized === self::LOGGED_IN) { if ($this->authorized === self::LOGGED_IN) {
$this->logger->logger(\danog\MadelineProto\Lang::$current_lang['already_loggedIn'], \danog\MadelineProto\Logger::NOTICE); $this->logger->logger(\danog\MadelineProto\Lang::$current_lang['already_loggedIn'], \danog\MadelineProto\Logger::NOTICE);
@ -61,7 +74,15 @@ trait Login
return $this->authorization; return $this->authorization;
} }
public function phoneLogin($number, $sms_type = 5) /**
* Login as user.
*
* @param string $number Phone number
* @param integer $sms_type SMS type
*
* @return \Generator
*/
public function phoneLogin($number, $sms_type = 5): \Generator
{ {
if ($this->authorized === self::LOGGED_IN) { if ($this->authorized === self::LOGGED_IN) {
$this->logger->logger(\danog\MadelineProto\Lang::$current_lang['already_loggedIn'], \danog\MadelineProto\Logger::NOTICE); $this->logger->logger(\danog\MadelineProto\Lang::$current_lang['already_loggedIn'], \danog\MadelineProto\Logger::NOTICE);
@ -80,7 +101,14 @@ trait Login
return $this->authorization; return $this->authorization;
} }
public function completePhoneLogin($code) /**
* Complet user login using login code.
*
* @param string $code Login code
*
* @return \Generator
*/
public function completePhoneLogin($code): \Generator
{ {
if ($this->authorized !== self::WAITING_CODE) { if ($this->authorized !== self::WAITING_CODE) {
throw new \danog\MadelineProto\Exception(\danog\MadelineProto\Lang::$current_lang['login_code_uncalled']); throw new \danog\MadelineProto\Exception(\danog\MadelineProto\Lang::$current_lang['login_code_uncalled']);
@ -131,7 +159,14 @@ trait Login
return $this->authorization; return $this->authorization;
} }
public function importAuthorization($authorization) /**
* Import authorization.
*
* @param mixed $authorization Authorization info
*
* @return \Generator
*/
public function importAuthorization($authorization): \Generator
{ {
if ($this->authorized === self::LOGGED_IN) { if ($this->authorized === self::LOGGED_IN) {
$this->logger->logger(\danog\MadelineProto\Lang::$current_lang['already_loggedIn'], \danog\MadelineProto\Logger::NOTICE); $this->logger->logger(\danog\MadelineProto\Lang::$current_lang['already_loggedIn'], \danog\MadelineProto\Logger::NOTICE);
@ -166,7 +201,12 @@ trait Login
return $res; return $res;
} }
public function exportAuthorization() /**
* Export authorization.
*
* @return \Generator<array>
*/
public function exportAuthorization(): \Generator
{ {
if ($this->authorized !== self::LOGGED_IN) { if ($this->authorized !== self::LOGGED_IN) {
throw new \danog\MadelineProto\Exception(\danog\MadelineProto\Lang::$current_lang['not_loggedIn']); throw new \danog\MadelineProto\Exception(\danog\MadelineProto\Lang::$current_lang['not_loggedIn']);
@ -177,7 +217,15 @@ trait Login
return [$this->datacenter->curdc, $this->datacenter->getDataCenterConnection($this->datacenter->curdc)->getPermAuthKey()->getAuthKey()]; return [$this->datacenter->curdc, $this->datacenter->getDataCenterConnection($this->datacenter->curdc)->getPermAuthKey()->getAuthKey()];
} }
public function completeSignup($first_name, $last_name) /**
* Complete signup to Telegram.
*
* @param string $first_name First name
* @param string $last_name Last name
*
* @return \Generator
*/
public function completeSignup(string $first_name, string $last_name = ''): \Generator
{ {
if ($this->authorized !== self::WAITING_SIGNUP) { if ($this->authorized !== self::WAITING_SIGNUP) {
throw new \danog\MadelineProto\Exception(\danog\MadelineProto\Lang::$current_lang['signup_uncalled']); throw new \danog\MadelineProto\Exception(\danog\MadelineProto\Lang::$current_lang['signup_uncalled']);
@ -196,7 +244,14 @@ trait Login
return $this->authorization; return $this->authorization;
} }
public function complete2faLogin($password) /**
* Complete 2FA login.
*
* @param string $password Password
*
* @return \Generator
*/
public function complete2faLogin(string $password): \Generator
{ {
if ($this->authorized !== self::WAITING_PASSWORD) { if ($this->authorized !== self::WAITING_PASSWORD) {
throw new \danog\MadelineProto\Exception(\danog\MadelineProto\Lang::$current_lang['2fa_uncalled']); throw new \danog\MadelineProto\Exception(\danog\MadelineProto\Lang::$current_lang['2fa_uncalled']);
@ -222,9 +277,10 @@ trait Login
* The params array can contain password, new_password, email and hint params. * The params array can contain password, new_password, email and hint params.
* *
* @param array $params The params * @param array $params The params
* @return void *
* @return \Generator
*/ */
public function update2fa(array $params) public function update2fa(array $params): \Generator
{ {
$hasher = new PasswordCalculator($this->logger); $hasher = new PasswordCalculator($this->logger);
$hasher->addInfo(yield $this->methodCallAsyncRead('account.getPassword', [], ['datacenter' => $this->datacenter->curdc])); $hasher->addInfo(yield $this->methodCallAsyncRead('account.getPassword', [], ['datacenter' => $this->datacenter->curdc]));

View File

@ -88,7 +88,7 @@ trait Start
exit; exit;
} }
public function webPhoneLogin() private function webPhoneLogin()
{ {
try { try {
yield $this->phoneLogin($_POST['phone_number']); yield $this->phoneLogin($_POST['phone_number']);
@ -100,7 +100,7 @@ trait Start
} }
} }
public function webCompletePhoneLogin() private function webCompletePhoneLogin()
{ {
try { try {
yield $this->completePhoneLogin($_POST['phone_code']); yield $this->completePhoneLogin($_POST['phone_code']);
@ -112,7 +112,7 @@ trait Start
} }
} }
public function webComplete2faLogin() private function webComplete2faLogin()
{ {
try { try {
yield $this->complete2faLogin($_POST['password']); yield $this->complete2faLogin($_POST['password']);
@ -124,7 +124,7 @@ trait Start
} }
} }
public function webCompleteSignup() private function webCompleteSignup()
{ {
try { try {
yield $this->completeSignup($_POST['first_name'], isset($_POST['last_name']) ? $_POST['last_name'] : ''); yield $this->completeSignup($_POST['first_name'], isset($_POST['last_name']) ? $_POST['last_name'] : '');
@ -136,7 +136,7 @@ trait Start
} }
} }
public function webBotLogin() private function webBotLogin()
{ {
try { try {
yield $this->botLogin($_POST['token']); yield $this->botLogin($_POST['token']);

View File

@ -23,7 +23,7 @@ use function Amp\ByteStream\getOutputBufferStream;
trait Templates trait Templates
{ {
public function webEcho(string $message = '') private function webEcho(string $message = '')
{ {
$stdout = getOutputBufferStream(); $stdout = getOutputBufferStream();
switch ($this->authorized) { switch ($this->authorized) {
@ -68,7 +68,7 @@ trait Templates
</body> </body>
</html>'; </html>';
public function webEchoTemplate($message, $form): string private function webEchoTemplate($message, $form): string
{ {
return \sprintf($this->web_template, $form, $message); return \sprintf($this->web_template, $form, $message);
} }

View File

@ -64,218 +64,221 @@ $settings = \json_decode(\getenv('MTPROTO_SETTINGS'), true) ?: [];
*/ */
echo 'Loading MadelineProto...'.PHP_EOL; echo 'Loading MadelineProto...'.PHP_EOL;
$MadelineProto = new \danog\MadelineProto\API(\getcwd().'/testing.madeline', $settings); $MadelineProto = new \danog\MadelineProto\API(\getcwd().'/testing.madeline', $settings);
$MadelineProto->fileGetContents('https://google.com'); $MadelineProto->async(true);
$MadelineProto->start(); $MadelineProto->loop(function () use ($MadelineProto) {
$MadelineProto->async(false); yield $MadelineProto->fileGetContents('https://google.com');
yield $MadelineProto->start();
try { try {
$MadelineProto->getSelf(); yield $MadelineProto->getSelf();
} catch (\danog\MadelineProto\Exception $e) { } catch (\danog\MadelineProto\Exception $e) {
if ($e->getMessage() === 'TOS action required, check the logs') { if ($e->getMessage() === 'TOS action required, check the logs') {
$MadelineProto->acceptTos(); yield $MadelineProto->acceptTos();
}
} }
}
//var_dump(count($MadelineProto->getPwrChat('@madelineproto')['participants'])); //var_dump(count($MadelineProto->getPwrChat('@madelineproto')['participants']));
/* /*
* Test logging * Test logging
*/ */
\danog\MadelineProto\Logger::log('hey', \danog\MadelineProto\Logger::ULTRA_VERBOSE); $MadelineProto->logger('hey', \danog\MadelineProto\Logger::ULTRA_VERBOSE);
\danog\MadelineProto\Logger::log('hey', \danog\MadelineProto\Logger::VERBOSE); $MadelineProto->logger('hey', \danog\MadelineProto\Logger::VERBOSE);
\danog\MadelineProto\Logger::log('hey', \danog\MadelineProto\Logger::NOTICE); $MadelineProto->logger('hey', \danog\MadelineProto\Logger::NOTICE);
\danog\MadelineProto\Logger::log('hey', \danog\MadelineProto\Logger::WARNING); $MadelineProto->logger('hey', \danog\MadelineProto\Logger::WARNING);
\danog\MadelineProto\Logger::log('hey', \danog\MadelineProto\Logger::ERROR); $MadelineProto->logger('hey', \danog\MadelineProto\Logger::ERROR);
\danog\MadelineProto\Logger::log('hey', \danog\MadelineProto\Logger::FATAL_ERROR); $MadelineProto->logger('hey', \danog\MadelineProto\Logger::FATAL_ERROR);
/** /**
* A small example message to use for tests. * A small example message to use for tests.
*/ */
$message = (\getenv('TRAVIS_COMMIT') == '') ? 'I iz works always (io laborare sembre) (yo lavorar siempre) (mi labori ĉiam) (я всегда работать) (Ik werkuh altijd) (Ngimbonga ngaso sonke isikhathi ukusebenza)' : ('Travis ci tests in progress: commit '.\getenv('TRAVIS_COMMIT').', job '.\getenv('TRAVIS_JOB_NUMBER').', PHP version: '.\getenv('TRAVIS_PHP_VERSION')); $message = (\getenv('TRAVIS_COMMIT') == '') ? 'I iz works always (io laborare sembre) (yo lavorar siempre) (mi labori ĉiam) (я всегда работать) (Ik werkuh altijd) (Ngimbonga ngaso sonke isikhathi ukusebenza)' : ('Travis ci tests in progress: commit '.\getenv('TRAVIS_COMMIT').', job '.\getenv('TRAVIS_JOB_NUMBER').', PHP version: '.\getenv('TRAVIS_PHP_VERSION'));
/* /*
* Try making a phone call * Try making a phone call
*/ */
if (!\getenv('TRAVIS_COMMIT') && \stripos($MadelineProto->readline('Do you want to make a call? (y/n): '), 'y') !== false) { if (!\getenv('TRAVIS_COMMIT') && \stripos(yield $MadelineProto->readline('Do you want to make a call? (y/n): '), 'y') !== false) {
$controller = $MadelineProto->requestCall(\getenv('TEST_SECRET_CHAT'))->play('input.raw')->then('input.raw')->playOnHold(['input.raw'])->setOutputFile('output.raw'); $controller = yield $MadelineProto->requestCall(\getenv('TEST_SECRET_CHAT'))->play('input.raw')->then('input.raw')->playOnHold(['input.raw'])->setOutputFile('output.raw');
while ($controller->getCallState() < \danog\MadelineProto\VoIP::CALL_STATE_READY) { while ($controller->getCallState() < \danog\MadelineProto\VoIP::CALL_STATE_READY) {
$MadelineProto->getUpdates(); yield $MadelineProto->getUpdates();
}
$MadelineProto->logger($controller->configuration);
while ($controller->getCallState() < \danog\MadelineProto\VoIP::CALL_STATE_ENDED) {
yield $MadelineProto->getUpdates();
}
} }
\danog\MadelineProto\Logger::log($controller->configuration);
while ($controller->getCallState() < \danog\MadelineProto\VoIP::CALL_STATE_ENDED) {
$MadelineProto->getUpdates();
}
}
/* /*
* Try receiving a phone call * Try receiving a phone call
*/ */
if (!\getenv('TRAVIS_COMMIT') && \stripos($MadelineProto->readline('Do you want to handle incoming calls? (y/n): '), 'y') !== false) { if (!\getenv('TRAVIS_COMMIT') && \stripos(yield $MadelineProto->readline('Do you want to handle incoming calls? (y/n): '), 'y') !== false) {
$howmany = $MadelineProto->readline('How many calls would you like me to handle? '); $howmany = yield $MadelineProto->readline('How many calls would you like me to handle? ');
$offset = 0; $offset = 0;
while ($howmany > 0) { while ($howmany > 0) {
$updates = $MadelineProto->getUpdates(['offset' => $offset, 'limit' => 50, 'timeout' => 0]); // Just like in the bot API, you can specify an offset, a limit and a timeout $updates = yield $MadelineProto->getUpdates(['offset' => $offset, 'limit' => 50, 'timeout' => 0]); // Just like in the bot API, you can specify an offset, a limit and a timeout
foreach ($updates as $update) { foreach ($updates as $update) {
\danog\MadelineProto\Logger::log($update); $MadelineProto->logger($update);
$offset = $update['update_id'] + 1; // Just like in the bot API, the offset must be set to the last update_id $offset = $update['update_id'] + 1; // Just like in the bot API, the offset must be set to the last update_id
switch ($update['update']['_']) { switch ($update['update']['_']) {
case 'updatePhoneCall': case 'updatePhoneCall':
if (\is_object($update['update']['phone_call']) && $update['update']['phone_call']->getCallState() === \danog\MadelineProto\VoIP::CALL_STATE_INCOMING) { if (\is_object($update['update']['phone_call']) && $update['update']['phone_call']->getCallState() === \danog\MadelineProto\VoIP::CALL_STATE_INCOMING) {
$update['update']['phone_call']->accept()->play('input.raw')->then('input.raw')->playOnHold(['input.raw'])->setOutputFile('output.raw'); $update['update']['phone_call']->accept()->play('input.raw')->then('input.raw')->playOnHold(['input.raw'])->setOutputFile('output.raw');
$howmany--; $howmany--;
} }
} }
}
} }
} }
}
/*
* Secret chat usage
*/
if (!\getenv('TRAVIS_COMMIT') && \stripos($MadelineProto->readline('Do you want to make the secret chat tests? (y/n): '), 'y') !== false) {
/**
* Request a secret chat.
*/
$secret_chat_id = $MadelineProto->requestSecretChat(\getenv('TEST_SECRET_CHAT'));
echo 'Waiting for '.\getenv('TEST_SECRET_CHAT').' (secret chat id '.$secret_chat_id.') to accept the secret chat...'.PHP_EOL;
/* /*
* Wait until the other party accepts it * Secret chat usage
*/ */
while ($MadelineProto->secretChatStatus($secret_chat_id) !== 2) { if (!\getenv('TRAVIS_COMMIT') && \stripos(yield $MadelineProto->readline('Do you want to make the secret chat tests? (y/n): '), 'y') !== false) {
$MadelineProto->getUpdates(); /**
} * Request a secret chat.
*/
$secret_chat_id = yield $MadelineProto->requestSecretChat(\getenv('TEST_SECRET_CHAT'));
echo 'Waiting for '.\getenv('TEST_SECRET_CHAT').' (secret chat id '.$secret_chat_id.') to accept the secret chat...'.PHP_EOL;
/** /*
* Send a markdown-formatted text message with expiration after 10 seconds. * Wait until the other party accepts it
*/ */
$sentMessage = $MadelineProto->messages->sendEncrypted([ while (yield $MadelineProto->secretChatStatus($secret_chat_id) !== 2) {
'peer' => $secret_chat_id, yield $MadelineProto->getUpdates();
'message' => [ }
'_' => 'decryptedMessage',
'media' => ['_' => 'decryptedMessageMediaEmpty'], // No media
'ttl' => 10, // This message self-destructs 10 seconds after reception
'message' => '```'.$message.'```', // Code Markdown
'parse_mode' => 'Markdown',
],
]);
\danog\MadelineProto\Logger::log($sentMessage, \danog\MadelineProto\Logger::NOTICE);
/** /**
* Send secret media. * Send a markdown-formatted text message with expiration after 10 seconds.
*/ */
$secret_media = []; $sentMessage = yield $MadelineProto->messages->sendEncrypted([
'peer' => $secret_chat_id,
'message' => [
'_' => 'decryptedMessage',
'media' => ['_' => 'decryptedMessageMediaEmpty'], // No media
'ttl' => 10, // This message self-destructs 10 seconds after reception
'message' => '```'.$message.'```', // Code Markdown
'parse_mode' => 'Markdown',
],
]);
$MadelineProto->logger($sentMessage, \danog\MadelineProto\Logger::NOTICE);
// Photo uploaded as document, secret chat /**
$secret_media['document_photo'] = [ * Send secret media.
'peer' => $secret_chat_id, */
'file' => 'tests/faust.jpg', // The file to send $secret_media = [];
'message' => [
'_' => 'decryptedMessage', // Photo uploaded as document, secret chat
'ttl' => 0, // This message does not self-destruct $secret_media['document_photo'] = [
'message' => '', // No text message, only media 'peer' => $secret_chat_id,
'media' => [ 'file' => 'tests/faust.jpg', // The file to send
'_' => 'decryptedMessageMediaDocument', 'message' => [
'thumb' => \file_get_contents('tests/faust.preview.jpg'), // The thumbnail must be generated manually, it must be in jpg format, 90x90 '_' => 'decryptedMessage',
'thumb_w' => 90, 'ttl' => 0, // This message does not self-destruct
'thumb_h' => 90, 'message' => '', // No text message, only media
'mime_type' => \mime_content_type('tests/faust.jpg'), // The file's mime type 'media' => [
'caption' => 'This file was uploaded using @MadelineProto', // The caption '_' => 'decryptedMessageMediaDocument',
'file_name' => 'faust.jpg', // The file's name 'thumb' => \file_get_contents('tests/faust.preview.jpg'), // The thumbnail must be generated manually, it must be in jpg format, 90x90
'size' => \filesize('tests/faust.jpg'), // The file's size 'thumb_w' => 90,
'attributes' => [ 'thumb_h' => 90,
['_' => 'documentAttributeImageSize', 'w' => 1280, 'h' => 914], // Image's resolution 'mime_type' => \mime_content_type('tests/faust.jpg'), // The file's mime type
'caption' => 'This file was uploaded using @MadelineProto', // The caption
'file_name' => 'faust.jpg', // The file's name
'size' => \filesize('tests/faust.jpg'), // The file's size
'attributes' => [
['_' => 'documentAttributeImageSize', 'w' => 1280, 'h' => 914], // Image's resolution
],
], ],
], ],
], ];
];
// Photo, secret chat // Photo, secret chat
$secret_media['photo'] = [ $secret_media['photo'] = [
'peer' => $secret_chat_id, 'peer' => $secret_chat_id,
'file' => 'tests/faust.jpg', 'file' => 'tests/faust.jpg',
'message' => [ 'message' => [
'_' => 'decryptedMessage', '_' => 'decryptedMessage',
'ttl' => 0, 'ttl' => 0,
'message' => '', 'message' => '',
'media' => [ 'media' => [
'_' => 'decryptedMessageMediaPhoto', '_' => 'decryptedMessageMediaPhoto',
'thumb' => \file_get_contents('tests/faust.preview.jpg'), 'thumb' => \file_get_contents('tests/faust.preview.jpg'),
'thumb_w' => 90, 'thumb_w' => 90,
'thumb_h' => 90, 'thumb_h' => 90,
'caption' => 'This file was uploaded using @MadelineProto', 'caption' => 'This file was uploaded using @MadelineProto',
'size' => \filesize('tests/faust.jpg'), 'size' => \filesize('tests/faust.jpg'),
'w' => 1280, 'w' => 1280,
'h' => 914, 'h' => 914,
],
], ],
], ];
];
// GIF, secret chat // GIF, secret chat
$secret_media['gif'] = ['peer' => $secret_chat_id, 'file' => 'tests/pony.mp4', 'message' => ['_' => 'decryptedMessage', 'ttl' => 0, 'message' => '', 'media' => ['_' => 'decryptedMessageMediaDocument', 'thumb' => \file_get_contents('tests/pony.preview.jpg'), 'thumb_w' => 90, 'thumb_h' => 90, 'mime_type' => \mime_content_type('tests/pony.mp4'), 'caption' => 'test', 'file_name' => 'pony.mp4', 'size' => \filesize('tests/faust.jpg'), 'attributes' => [['_' => 'documentAttributeAnimated']]]]]; $secret_media['gif'] = ['peer' => $secret_chat_id, 'file' => 'tests/pony.mp4', 'message' => ['_' => 'decryptedMessage', 'ttl' => 0, 'message' => '', 'media' => ['_' => 'decryptedMessageMediaDocument', 'thumb' => \file_get_contents('tests/pony.preview.jpg'), 'thumb_w' => 90, 'thumb_h' => 90, 'mime_type' => \mime_content_type('tests/pony.mp4'), 'caption' => 'test', 'file_name' => 'pony.mp4', 'size' => \filesize('tests/faust.jpg'), 'attributes' => [['_' => 'documentAttributeAnimated']]]]];
// Sticker, secret chat // Sticker, secret chat
$secret_media['sticker'] = ['peer' => $secret_chat_id, 'file' => 'tests/lel.webp', 'message' => ['_' => 'decryptedMessage', 'ttl' => 0, 'message' => '', 'media' => ['_' => 'decryptedMessageMediaDocument', 'thumb' => \file_get_contents('tests/lel.preview.jpg'), 'thumb_w' => 90, 'thumb_h' => 90, 'mime_type' => \mime_content_type('tests/lel.webp'), 'caption' => 'test', 'file_name' => 'lel.webp', 'size' => \filesize('tests/lel.webp'), 'attributes' => [['_' => 'documentAttributeSticker', 'alt' => 'LEL', 'stickerset' => ['_' => 'inputStickerSetEmpty']]]]]]; $secret_media['sticker'] = ['peer' => $secret_chat_id, 'file' => 'tests/lel.webp', 'message' => ['_' => 'decryptedMessage', 'ttl' => 0, 'message' => '', 'media' => ['_' => 'decryptedMessageMediaDocument', 'thumb' => \file_get_contents('tests/lel.preview.jpg'), 'thumb_w' => 90, 'thumb_h' => 90, 'mime_type' => \mime_content_type('tests/lel.webp'), 'caption' => 'test', 'file_name' => 'lel.webp', 'size' => \filesize('tests/lel.webp'), 'attributes' => [['_' => 'documentAttributeSticker', 'alt' => 'LEL', 'stickerset' => ['_' => 'inputStickerSetEmpty']]]]]];
// Document, secret chat // Document, secret chat
$secret_media['document'] = ['peer' => $secret_chat_id, 'file' => 'tests/60', 'message' => ['_' => 'decryptedMessage', 'ttl' => 0, 'message' => '', 'media' => ['_' => 'decryptedMessageMediaDocument', 'thumb' => \file_get_contents('tests/faust.preview.jpg'), 'thumb_w' => 90, 'thumb_h' => 90, 'mime_type' => 'magic/magic', 'caption' => 'test', 'file_name' => 'magic.magic', 'size' => \filesize('tests/60'), 'attributes' => [['_' => 'documentAttributeFilename', 'file_name' => 'fairy']]]]]; $secret_media['document'] = ['peer' => $secret_chat_id, 'file' => 'tests/60', 'message' => ['_' => 'decryptedMessage', 'ttl' => 0, 'message' => '', 'media' => ['_' => 'decryptedMessageMediaDocument', 'thumb' => \file_get_contents('tests/faust.preview.jpg'), 'thumb_w' => 90, 'thumb_h' => 90, 'mime_type' => 'magic/magic', 'caption' => 'test', 'file_name' => 'magic.magic', 'size' => \filesize('tests/60'), 'attributes' => [['_' => 'documentAttributeFilename', 'file_name' => 'fairy']]]]];
// Video, secret chat // Video, secret chat
$secret_media['video'] = ['peer' => $secret_chat_id, 'file' => 'tests/swing.mp4', 'message' => ['_' => 'decryptedMessage', 'ttl' => 0, 'message' => '', 'media' => ['_' => 'decryptedMessageMediaDocument', 'thumb' => \file_get_contents('tests/swing.preview.jpg'), 'thumb_w' => 90, 'thumb_h' => 90, 'mime_type' => \mime_content_type('tests/swing.mp4'), 'caption' => 'test', 'file_name' => 'swing.mp4', 'size' => \filesize('tests/swing.mp4'), 'attributes' => [['_' => 'documentAttributeVideo']]]]]; $secret_media['video'] = ['peer' => $secret_chat_id, 'file' => 'tests/swing.mp4', 'message' => ['_' => 'decryptedMessage', 'ttl' => 0, 'message' => '', 'media' => ['_' => 'decryptedMessageMediaDocument', 'thumb' => \file_get_contents('tests/swing.preview.jpg'), 'thumb_w' => 90, 'thumb_h' => 90, 'mime_type' => \mime_content_type('tests/swing.mp4'), 'caption' => 'test', 'file_name' => 'swing.mp4', 'size' => \filesize('tests/swing.mp4'), 'attributes' => [['_' => 'documentAttributeVideo']]]]];
// audio, secret chat // audio, secret chat
$secret_media['audio'] = ['peer' => $secret_chat_id, 'file' => 'tests/mosconi.mp3', 'message' => ['_' => 'decryptedMessage', 'ttl' => 0, 'message' => '', 'media' => ['_' => 'decryptedMessageMediaDocument', 'thumb' => \file_get_contents('tests/faust.preview.jpg'), 'thumb_w' => 90, 'thumb_h' => 90, 'mime_type' => \mime_content_type('tests/mosconi.mp3'), 'caption' => 'test', 'file_name' => 'mosconi.mp3', 'size' => \filesize('tests/mosconi.mp3'), 'attributes' => [['_' => 'documentAttributeAudio', 'voice' => false, 'title' => 'AH NON LO SO IO', 'performer' => 'IL DIO GERMANO MOSCONI']]]]]; $secret_media['audio'] = ['peer' => $secret_chat_id, 'file' => 'tests/mosconi.mp3', 'message' => ['_' => 'decryptedMessage', 'ttl' => 0, 'message' => '', 'media' => ['_' => 'decryptedMessageMediaDocument', 'thumb' => \file_get_contents('tests/faust.preview.jpg'), 'thumb_w' => 90, 'thumb_h' => 90, 'mime_type' => \mime_content_type('tests/mosconi.mp3'), 'caption' => 'test', 'file_name' => 'mosconi.mp3', 'size' => \filesize('tests/mosconi.mp3'), 'attributes' => [['_' => 'documentAttributeAudio', 'voice' => false, 'title' => 'AH NON LO SO IO', 'performer' => 'IL DIO GERMANO MOSCONI']]]]];
$secret_media['voice'] = ['peer' => $secret_chat_id, 'file' => 'tests/mosconi.mp3', 'message' => ['_' => 'decryptedMessage', 'ttl' => 0, 'message' => '', 'media' => ['_' => 'decryptedMessageMediaDocument', 'thumb' => \file_get_contents('tests/faust.preview.jpg'), 'thumb_w' => 90, 'thumb_h' => 90, 'mime_type' => \mime_content_type('tests/mosconi.mp3'), 'caption' => 'test', 'file_name' => 'mosconi.mp3', 'size' => \filesize('tests/mosconi.mp3'), 'attributes' => [['_' => 'documentAttributeAudio', 'voice' => true, 'title' => 'AH NON LO SO IO', 'performer' => 'IL DIO GERMANO MOSCONI']]]]]; $secret_media['voice'] = ['peer' => $secret_chat_id, 'file' => 'tests/mosconi.mp3', 'message' => ['_' => 'decryptedMessage', 'ttl' => 0, 'message' => '', 'media' => ['_' => 'decryptedMessageMediaDocument', 'thumb' => \file_get_contents('tests/faust.preview.jpg'), 'thumb_w' => 90, 'thumb_h' => 90, 'mime_type' => \mime_content_type('tests/mosconi.mp3'), 'caption' => 'test', 'file_name' => 'mosconi.mp3', 'size' => \filesize('tests/mosconi.mp3'), 'attributes' => [['_' => 'documentAttributeAudio', 'voice' => true, 'title' => 'AH NON LO SO IO', 'performer' => 'IL DIO GERMANO MOSCONI']]]]];
foreach ($secret_media as $type => $smessage) { foreach ($secret_media as $type => $smessage) {
\danog\MadelineProto\Logger::log("Encrypting and uploading $type..."); $MadelineProto->logger("Encrypting and uploading $type...");
$type = $MadelineProto->messages->sendEncryptedFile($smessage); $type = yield $MadelineProto->messages->sendEncryptedFile($smessage);
}
} }
}
$mention = $MadelineProto->getInfo(\getenv('TEST_USERNAME')); // Returns an array with all of the constructors that can be extracted from a username or an id $mention = yield $MadelineProto->getInfo(\getenv('TEST_USERNAME')); // Returns an array with all of the constructors that can be extracted from a username or an id
$mention = $mention['user_id']; // Selects only the numeric user id $mention = $mention['user_id']; // Selects only the numeric user id
$media = []; $media = [];
// Image // Image
$media['photo'] = ['_' => 'inputMediaUploadedPhoto', 'file' => 'tests/faust.jpg']; $media['photo'] = ['_' => 'inputMediaUploadedPhoto', 'file' => 'tests/faust.jpg'];
// Sticker // Sticker
$media['sticker'] = ['_' => 'inputMediaUploadedDocument', 'file' => 'tests/lel.webp', 'attributes' => [['_' => 'documentAttributeSticker', 'alt' => 'LEL']]]; $media['sticker'] = ['_' => 'inputMediaUploadedDocument', 'file' => 'tests/lel.webp', 'attributes' => [['_' => 'documentAttributeSticker', 'alt' => 'LEL']]];
// Video // Video
$media['video'] = ['_' => 'inputMediaUploadedDocument', 'file' => 'tests/swing.mp4', 'attributes' => [['_' => 'documentAttributeVideo']]]; $media['video'] = ['_' => 'inputMediaUploadedDocument', 'file' => 'tests/swing.mp4', 'attributes' => [['_' => 'documentAttributeVideo']]];
// audio // audio
$media['audio'] = ['_' => 'inputMediaUploadedDocument', 'file' => 'tests/mosconi.mp3', 'attributes' => [['_' => 'documentAttributeAudio', 'voice' => false, 'title' => 'AH NON LO SO IO', 'performer' => 'IL DIO GERMANO MOSCONI']]]; $media['audio'] = ['_' => 'inputMediaUploadedDocument', 'file' => 'tests/mosconi.mp3', 'attributes' => [['_' => 'documentAttributeAudio', 'voice' => false, 'title' => 'AH NON LO SO IO', 'performer' => 'IL DIO GERMANO MOSCONI']]];
// voice // voice
$media['voice'] = ['_' => 'inputMediaUploadedDocument', 'file' => 'tests/mosconi.mp3', 'attributes' => [['_' => 'documentAttributeAudio', 'voice' => true, 'title' => 'AH NON LO SO IO', 'performer' => 'IL DIO GERMANO MOSCONI']]]; $media['voice'] = ['_' => 'inputMediaUploadedDocument', 'file' => 'tests/mosconi.mp3', 'attributes' => [['_' => 'documentAttributeAudio', 'voice' => true, 'title' => 'AH NON LO SO IO', 'performer' => 'IL DIO GERMANO MOSCONI']]];
// Document // Document
$media['document'] = ['_' => 'inputMediaUploadedDocument', 'file' => 'tests/60', 'mime_type' => 'magic/magic', 'attributes' => [['_' => 'documentAttributeFilename', 'file_name' => 'magic.magic']]]; $media['document'] = ['_' => 'inputMediaUploadedDocument', 'file' => 'tests/60', 'mime_type' => 'magic/magic', 'attributes' => [['_' => 'documentAttributeFilename', 'file_name' => 'magic.magic']]];
$message = 'yay '.\PHP_VERSION_ID; $message = 'yay '.\PHP_VERSION_ID;
$mention = $MadelineProto->getInfo(\getenv('TEST_USERNAME')); // Returns an array with all of the constructors that can be extracted from a username or an id $mention = yield $MadelineProto->getInfo(\getenv('TEST_USERNAME')); // Returns an array with all of the constructors that can be extracted from a username or an id
$mention = $mention['user_id']; // Selects only the numeric user id $mention = $mention['user_id']; // Selects only the numeric user id
/* /*
$t = time(); $t = time();
$MadelineProto->upload('big'); yield $MadelineProto->upload('big');
var_dump(time()-$t); var_dump(time()-$t);
*/ */
foreach (\json_decode(\getenv('TEST_DESTINATION_GROUPS'), true) as $peer) { foreach (\json_decode(\getenv('TEST_DESTINATION_GROUPS'), true) as $peer) {
$sentMessage = $MadelineProto->messages->sendMessage(['peer' => $peer, 'message' => $message, 'entities' => [['_' => 'inputMessageEntityMentionName', 'offset' => 0, 'length' => \mb_strlen($message), 'user_id' => $mention]]]); $sentMessage = yield $MadelineProto->messages->sendMessage(['peer' => $peer, 'message' => $message, 'entities' => [['_' => 'inputMessageEntityMentionName', 'offset' => 0, 'length' => \mb_strlen($message), 'user_id' => $mention]]]);
\danog\MadelineProto\Logger::log($sentMessage, \danog\MadelineProto\Logger::NOTICE); $MadelineProto->logger($sentMessage, \danog\MadelineProto\Logger::NOTICE);
foreach ($media as $type => $inputMedia) { foreach ($media as $type => $inputMedia) {
\danog\MadelineProto\Logger::log("Sending $type"); $MadelineProto->logger("Sending $type");
$type = $MadelineProto->messages->sendMedia(['peer' => $peer, 'media' => $inputMedia, 'message' => '['.$message.'](mention:'.$mention.')', 'parse_mode' => 'markdown']); $type = yield $MadelineProto->messages->sendMedia(['peer' => $peer, 'media' => $inputMedia, 'message' => '['.$message.'](mention:'.$mention.')', 'parse_mode' => 'markdown']);
yield $MadelineProto->downloadToDir(yield $MadelineProto->messages->uploadMedia(['peer' => '@me', 'media' => $inputMedia]), '/tmp');
}
} }
}
foreach (\json_decode(\getenv('TEST_DESTINATION_GROUPS'), true) as $peer) { foreach (\json_decode(\getenv('TEST_DESTINATION_GROUPS'), true) as $peer) {
$sentMessage = $MadelineProto->messages->sendMessage(['peer' => $peer, 'message' => $message, 'entities' => [['_' => 'inputMessageEntityMentionName', 'offset' => 0, 'length' => \mb_strlen($message), 'user_id' => $mention]]]); $sentMessage = yield $MadelineProto->messages->sendMessage(['peer' => $peer, 'message' => $message, 'entities' => [['_' => 'inputMessageEntityMentionName', 'offset' => 0, 'length' => \mb_strlen($message), 'user_id' => $mention]]]);
\danog\MadelineProto\Logger::log($sentMessage, \danog\MadelineProto\Logger::NOTICE); $MadelineProto->logger($sentMessage, \danog\MadelineProto\Logger::NOTICE);
} }
});