diff --git a/docs b/docs index ddb2e4f7..dc05dc5c 160000 --- a/docs +++ b/docs @@ -1 +1 @@ -Subproject commit ddb2e4f76938b69ceac6e4615901c642accae1ef +Subproject commit dc05dc5cebfcec90ac7851928c522a4d635dbab6 diff --git a/magna.php b/magna.php index 1c41df55..c1959b54 100755 --- a/magna.php +++ b/magna.php @@ -38,8 +38,117 @@ $MadelineProto->inputEncryptedFileVideo = $MadelineProto->upload_encrypted('test $MadelineProto->inputEncryptedFileAudio = $MadelineProto->upload_encrypted('tests/mosconi.mp3'); }*/ +use danog\MadelineProto\Loop\Impl\ResumableSignalLoop; +class MessageLoop extends ResumableSignalLoop +{ + const INTERVAL = 10; + private $timeout; + private $call; + public function __construct($API, $call, $timeout = self::INTERVAL) + { + $this->API = $API; + $this->call = $call; + $this->timeout = $timeout; + } + public function loop() + { + $MadelineProto = $this->API; + $logger = &$MadelineProto->logger; + + while (true) { + $result = yield $this->waitSignal($this->pause($this->timeout)); + if ($result) { + $logger->logger("Got signal in $this, exiting"); + return; + } + + try { + yield $MadelineProto->messages->editMessage(['id' => $this->call->mId, 'peer' => $this->call->getOtherID(), 'message' => 'Total running calls: '.count($MadelineProto->getEventHandler()->calls).PHP_EOL.PHP_EOL.$this->call->getDebugString()]); + } catch (\danog\MadelineProto\RPCErrorException $e) { + $MadelineProto->logger($e); + } + } + } + public function __toString(): string + { + return "VoIP message loop ".$this->call->getOtherId(); + } +} +class StatusLoop extends ResumableSignalLoop +{ + const INTERVAL = 2; + private $timeout; + private $call; + public function __construct($API, $call, $timeout = self::INTERVAL) + { + $this->API = $API; + $this->call = $call; + $this->timeout = $timeout; + } + public function loop() + { + $MadelineProto = $this->API; + $logger = &$MadelineProto->logger; + $call = $this->call; + + while (true) { + $result = yield $this->waitSignal($this->pause($this->timeout)); + if ($result) { + $logger->logger("Got signal in $this, exiting"); + return; + } + + if ($call->getCallState() === \danog\MadelineProto\VoIP::CALL_STATE_ENDED) { + try { + /*yield $this->messages->sendMedia([ + 'reply_to_msg_id' => $this->times[$call->getOtherID()][1], + 'peer' => $call->getOtherID(), 'message' => 'Call statistics by @magnaluna', + 'media' => [ + '_' => 'inputMediaUploadedDocument', + 'file' => "/tmp/stats".$call->getCallID()['id'].".txt", + 'attributes' => [ + ['_' => 'documentAttributeFilename', 'file_name' => "stats".$call->getCallID()['id'].".txt"] + ] + ], + ]);*/ + yield $MadelineProto->messages->sendMedia([ + 'reply_to_msg_id' => $this->call->mId, + 'peer' => $call->getOtherID(), 'message' => 'Debug info by @magnaluna', + 'media' => [ + '_' => 'inputMediaUploadedDocument', + 'file' => '/tmp/logs'.$call->getCallID()['id'].'.log', + 'attributes' => [ + ['_' => 'documentAttributeFilename', 'file_name' => 'logs'.$call->getCallID()['id'].'.log'], + ], + ], + ]); + } catch (\danog\MadelineProto\Exception $e) { + $MadelineProto->logger($e); + } catch (\danog\MadelineProto\RPCErrorException $e) { + $MadelineProto->logger($e); + } catch (\danog\MadelineProto\Exception $e) { + $MadelineProto->logger($e); + } + @unlink('/tmp/logs'.$call->getCallID()['id'].'.log'); + @unlink('/tmp/stats'.$call->getCallID()['id'].'.txt'); + $MadelineProto->getEventHandler()->cleanUpCall($call->getOtherID()); + return; + } + } + } + public function __toString(): string + { + return "VoIP status loop ".$this->call->getOtherId(); + } +} + class EventHandler extends \danog\MadelineProto\EventHandler { + const ADMINS = [101374607]; // @danogentili, creator of MadelineProto + private $messageLoops = []; + private $statusLoops = []; + private $programmed_call; + private $my_users; public function configureCall($call) { include 'songs.php'; @@ -50,16 +159,74 @@ class EventHandler extends \danog\MadelineProto\EventHandler //$call->configuration["stats_dump_file_path"] = "/tmp/stats".$call->getCallID()['id'].".txt"; // Default is /dev/null $call->parseConfig(); $call->playOnHold($songs); - //$this->messages->sendMessage(['message' => var_export($call->configuration, true), 'peer' => $call->getOtherID()]); + if ($call->getCallState() === \danog\MadelineProto\VoIP::CALL_STATE_INCOMING) { + if ($call->accept() === false) { + $MadelineProto->logger('DID NOT ACCEPT A CALL'); + } + } + if ($call->getCallState() !== \danog\MadelineProto\VoIP::CALL_STATE_ENDED) { + $this->calls[$call->getOtherID()] = $call; + try { + $call->mId = yield $this->messages->sendMessage(['peer' => $call->getOtherID(), 'message' => 'Total running calls: '.count($this->calls).PHP_EOL.PHP_EOL.$call->getDebugString()])['id']; + } catch (\Throwable $e) { + $this->logger($e); + } + $this->messageLoops[$call->getOtherID()] = new MessageLoop($this, $call); + $this->statusLoops[$call->getOtherID()] = new StatusLoop($this, $call); + $this->messageLoops[$call->getOtherID()]->start(); + $this->statusLoops[$call->getOtherID()]->start(); + } + //yield $this->messages->sendMessage(['message' => var_export($call->configuration, true), 'peer' => $call->getOtherID()]); + } + public function cleanUpCall($user) + { + if (isset($this->calls[$user])) { + unset($this->calls[$user]); + } + if (isset($this->messageLoops[$user])) { + $this->messageLoops[$user]->signal(true); + unset($this->messageLoops[$user]); + } + if (isset($this->statusLoops[$user])) { + $this->statusLoops[$user]->signal(true); + unset($this->statusLoops[$user]); + } + } + public function makeCall($user) + { + try { + if (isset($this->calls[$user])) { + if ($this->calls[$user]->getCallState() === \danog\MadelineProto\VoIP::CALL_STATE_ENDED) { + yield $this->cleanUpCall($user); + } else { + yield $this->messages->sendMessage(['peer' => $user, 'message' => "I'm already in a call with you!"]); + return; + } + } + yield $this->configureCall(yield $this->requestCall($user)); + } catch (\danog\MadelineProto\RPCErrorException $e) { + try { + if ($e->rpc === 'USER_PRIVACY_RESTRICTED') { + $e = 'Please disable call privacy settings to make me call you'; + }/* elseif (strpos($e->rpc, 'FLOOD_WAIT_') === 0) { + $t = str_replace('FLOOD_WAIT_', '', $e->rpc); + $this->programmed_call[] = [$user, time() + 1 + $t]; + $e = "I'll call you back in $t seconds.\nYou can also call me right now."; + }*/ + yield $this->messages->sendMessage(['peer' => $user, 'message' => (string) $e]); + } catch (\danog\MadelineProto\RPCErrorException $e) { + } + } catch (\Throwable $e) { + yield $this->messages->sendMessage(['peer' => $user, 'message' => (string) $e]); + } } - public function handleMessage($chat_id, $from_id, $message) { try { if (!isset($this->my_users[$from_id]) || $message === '/start') { $this->my_users[$from_id] = true; $message = '/call'; - $this->messages->sendMessage(['no_webpage' => true, 'peer' => $chat_id, 'message' => "Hi, I'm @magnaluna the webradio. + yield $this->messages->sendMessage(['no_webpage' => true, 'peer' => $chat_id, 'message' => "Hi, I'm @magnaluna the webradio. Call _me_ to listen to some **awesome** music, or send /call to make _me_ call _you_ (don't forget to disable call privacy settings!). @@ -80,50 +247,48 @@ Source code: https://github.com/danog/MadelineProto Propic art by @magnaluna on [deviantart](https://magnaluna.deviantart.com).", 'parse_mode' => 'Markdown']); } if (!isset($this->calls[$from_id]) && $message === '/call') { - $call = $this->request_call($from_id); - $this->configureCall($call); - if ($call->getCallState() !== \danog\MadelineProto\VoIP::CALL_STATE_ENDED) { - $this->calls[$call->getOtherID()] = $call; - $this->times[$call->getOtherID()] = [time(), $this->messages->sendMessage(['peer' => $call->getOtherID(), 'message' => 'Total running calls: '.count($this->calls).PHP_EOL.PHP_EOL.$call->getDebugString()])['id']]; - } + yield $this->makeCall($from_id); } if (strpos($message, '/program') === 0) { $time = strtotime(str_replace('/program ', '', $message)); if ($time === false) { - $this->messages->sendMessage(['peer' => $chat_id, 'message' => 'Invalid time provided']); + yield $this->messages->sendMessage(['peer' => $chat_id, 'message' => 'Invalid time provided']); + } else if ($time - time() <= 0) { + yield $this->messages->sendMessage(['peer' => $chat_id, 'message' => 'Invalid time provided']); } else { + yield $this->messages->sendMessage(['peer' => $chat_id, 'message' => 'OK']); $this->programmed_call[] = [$from_id, $time]; - $this->messages->sendMessage(['peer' => $chat_id, 'message' => 'OK']); + $key = count($this->programmed_call) - 1; + yield $this->sleep($time - time()); + yield $this->makeCall($from_id); + unset($this->programmed_call[$key]); } } - if ($message === '/broadcast' && $from_id === 101374607) { + if ($message === '/broadcast' && in_array(self::ADMINS, $from_id)) { $time = time() + 100; $message = explode(' ', $message, 2); unset($message[0]); $message = implode(' ', $message); - foreach ($this->get_dialogs() as $peer) { - $this->times_messages[] = [$peer, $time, $message]; - if (isset($peer['user_id'])) { - $this->programmed_call[] = [$peer['user_id'], $time]; - } - $time += 30; + $params = ['multiple' => true]; + foreach (yield $this->get_dialogs() as $peer) { + $params []= ['peer' => $peer, 'message' => $message]; } + yield $this->messages->sendMessage($params); } } catch (\danog\MadelineProto\RPCErrorException $e) { try { if ($e->rpc === 'USER_PRIVACY_RESTRICTED') { $e = 'Please disable call privacy settings to make me call you'; - } elseif (strpos($e->rpc, 'FLOOD_WAIT_') === 0) { + } /*elseif (strpos($e->rpc, 'FLOOD_WAIT_') === 0) { $t = str_replace('FLOOD_WAIT_', '', $e->rpc); - $this->programmed_call[] = [$from_id, time() + 1 + $t]; - $e = "Too many people used the /call function. I'll call you back in $t seconds.\nYou can also call me right now."; - } - $this->messages->sendMessage(['peer' => $chat_id, 'message' => (string) $e]); + $e = "Too many people used the /call function. I'll be able to call you in $t seconds.\nYou can also call me right now"; + }*/ + yield $this->messages->sendMessage(['peer' => $chat_id, 'message' => (string) $e]); } catch (\danog\MadelineProto\RPCErrorException $e) { } - echo $e; + $this->logger($e); } catch (\danog\MadelineProto\Exception $e) { - echo $e; + $this->logger($e); } } @@ -132,19 +297,19 @@ Propic art by @magnaluna on [deviantart](https://magnaluna.deviantart.com).", 'p if ($update['message']['out'] || $update['message']['to_id']['_'] !== 'peerUser' || !isset($update['message']['from_id'])) { return; } - \danog\MadelineProto\Logger::log($update); - $chat_id = $from_id = $this->get_info($update)['bot_api_id']; - $message = isset($update['message']['message']) ? $update['message']['message'] : ''; - $this->handleMessage($chat_id, $from_id, $message); + $this->logger->logger($update); + $chat_id = $from_id = yield $this->get_info($update)['bot_api_id']; + $message = $update['message']['message'] ?? ''; + yield $this->handleMessage($chat_id, $from_id, $message); } public function onUpdateNewEncryptedMessage($update) { return; - $chat_id = $this->get_info($update)['InputEncryptedChat']; - $from_id = $this->get_secret_chat($chat_id)['user_id']; + $chat_id = yield $this->get_info($update)['InputEncryptedChat']; + $from_id = yield $this->get_secret_chat($chat_id)['user_id']; $message = isset($update['message']['decrypted_message']['message']) ? $update['message']['decrypted_message']['message'] : ''; - $this->handleMessage($chat_id, $from_id, $message); + yield $this->handleMessage($chat_id, $from_id, $message); } public function onUpdateEncryption($update) @@ -155,132 +320,45 @@ Propic art by @magnaluna on [deviantart](https://magnaluna.deviantart.com).", 'p if ($update['chat']['_'] !== 'encryptedChat') { return; } - $chat_id = $this->get_info($update)['InputEncryptedChat']; - $from_id = $this->get_secret_chat($chat_id)['user_id']; + $chat_id = yield $this->get_info($update)['InputEncryptedChat']; + $from_id = yield $this->get_secret_chat($chat_id)['user_id']; $message = ''; } catch (\danog\MadelineProto\Exception $e) { return; } - $this->handleMessage($chat_id, $from_id, $message); + yield $this->handleMessage($chat_id, $from_id, $message); } public function onUpdatePhoneCall($update) { if (is_object($update['phone_call']) && isset($update['phone_call']->madeline) && $update['phone_call']->getCallState() === \danog\MadelineProto\VoIP::CALL_STATE_INCOMING) { - $this->configureCall($update['phone_call']); - if ($update['phone_call']->accept() === false) { - echo 'DID NOT ACCEPT A CALL'; - } - $this->calls[$update['phone_call']->getOtherID()] = $update['phone_call']; - - try { - $this->times[$update['phone_call']->getOtherID()] = [time(), $this->messages->sendMessage(['peer' => $update['phone_call']->getOtherID(), 'message' => 'Total running calls: '.count($this->calls).PHP_EOL.PHP_EOL])['id']]; - } catch (\danog\MadelineProto\RPCErrorException $e) { - } + yield $this->configureCall($update['phone_call']); } } - public function onAny($update) + /*public function onAny($update) { - \danog\MadelineProto\Logger::log($update); - } + $this->logger->logger($update); + }*/ - public function onLoop() + public function __construct($API) { - foreach ($this->programmed_call as $key => $pair) { - list($user, $time) = $pair; - if ($time < time()) { - if (!isset($this->calls[$user])) { - try { - $call = $this->request_call($user); - $this->configureCall($call); - if ($call->getCallState() !== \danog\MadelineProto\VoIP::CALL_STATE_ENDED) { - $this->calls[$call->getOtherID()] = $call; - $this->times[$call->getOtherID()] = [time(), $this->messages->sendMessage(['peer' => $call->getOtherID(), 'message' => 'Total running calls: '.count($this->calls).PHP_EOL.PHP_EOL.$call->getDebugString()])['id']]; - } - } catch (\danog\MadelineProto\RPCErrorException $e) { - try { - if ($e->rpc === 'USER_PRIVACY_RESTRICTED') { - $e = 'Please disable call privacy settings to make me call you'; - } elseif (strpos($e->rpc, 'FLOOD_WAIT_') === 0) { - $t = str_replace('FLOOD_WAIT_', '', $e->rpc); - $this->programmed_call[] = [$user, time() + 1 + $t]; - $e = "I'll call you back in $t seconds.\nYou can also call me right now."; - } - $this->messages->sendMessage(['peer' => $user, 'message' => (string) $e]); - } catch (\danog\MadelineProto\RPCErrorException $e) { - } - } - } + parent::__construct($API); + $this->programmed_call = []; + foreach ($this->programmed_call as $key => list($user, $time)) { + continue; + $sleepTime = $time <= time() ? 0 : $time - time(); + $this->callFork((function () use ($sleepTime, $key, $user) { + yield $this->sleep($sleepTime); + yield $this->makeCall($user); unset($this->programmed_call[$key]); - } - break; - } - foreach ($this->times_messages as $key => $pair) { - list($peer, $time, $message) = $pair; - if ($time < time()) { - try { - $this->messages->sendMessage(['peer' => $peer, 'message' => $message]); - } catch (\danog\MadelineProto\RPCErrorException $e) { - if (strpos($e->rpc, 'FLOOD_WAIT_') === 0) { - $t = str_replace('FLOOD_WAIT_', '', $e->rpc); - $this->times_messages[] = [$peer, time() + 1 + $t, $message]; - } - echo $e; - } - unset($this->times_messages[$key]); - } - break; - } - \danog\MadelineProto\Logger::log(count($this->calls).' calls running!'); - foreach ($this->calls as $key => $call) { - if ($call->getCallState() === \danog\MadelineProto\VoIP::CALL_STATE_ENDED) { - try { - if (isset($this->times[$call->getOtherID()][1])) { - /*$this->messages->sendMedia([ - 'reply_to_msg_id' => $this->times[$call->getOtherID()][1], - 'peer' => $call->getOtherID(), 'message' => 'Call statistics by @magnaluna', - 'media' => [ - '_' => 'inputMediaUploadedDocument', - 'file' => "/tmp/stats".$call->getCallID()['id'].".txt", - 'attributes' => [ - ['_' => 'documentAttributeFilename', 'file_name' => "stats".$call->getCallID()['id'].".txt"] - ] - ], - ]);*/ - $this->messages->sendMedia([ - 'reply_to_msg_id' => $this->times[$call->getOtherID()][1], - 'peer' => $call->getOtherID(), 'message' => 'Debug info by @magnaluna', - 'media' => [ - '_' => 'inputMediaUploadedDocument', - 'file' => '/tmp/logs'.$call->getCallID()['id'].'.log', - 'attributes' => [ - ['_' => 'documentAttributeFilename', 'file_name' => 'logs'.$call->getCallID()['id'].'.log'], - ], - ], - ]); - } - } catch (\danog\MadelineProto\Exception $e) { - echo $e; - } catch (\danog\MadelineProto\RPCErrorException $e) { - echo $e; - } catch (\danog\MadelineProto\Exception $e) { - echo $e; - } - @unlink('/tmp/logs'.$call->getCallID()['id'].'.log'); - @unlink('/tmp/stats'.$call->getCallID()['id'].'.txt'); - unset($this->calls[$key]); - } elseif (isset($this->times[$call->getOtherID()]) && $this->times[$call->getOtherID()][0] < time()) { - $this->times[$call->getOtherID()][0] += 30 + count($this->calls); - - try { - $this->messages->editMessage(['id' => $this->times[$call->getOtherID()][1], 'peer' => $call->getOtherID(), 'message' => 'Total running calls: '.count($this->calls).PHP_EOL.PHP_EOL.$call->getDebugString()]); - } catch (\danog\MadelineProto\RPCErrorException $e) { - echo $e; - } - } + })()); } } + public function __sleep() + { + return ['programmed_call', 'my_users']; + } } if (!class_exists('\\danog\\MadelineProto\\VoIPServerConfig')) { @@ -295,16 +373,11 @@ if (!class_exists('\\danog\\MadelineProto\\VoIPServerConfig')) { 'audio_congestion_window' => 4 * 1024, ] ); -$MadelineProto = new \danog\MadelineProto\API('session.madeline', ['secret_chats' => ['accept_chats' => false], 'logger' => ['logger' => 3, 'logger_level' => 5, 'logger_param' => getcwd().'/MadelineProto.log']]); +$MadelineProto = new \danog\MadelineProto\API('session.madeline', ['secret_chats' => ['accept_chats' => false], 'logger' => ['logger' => 3, 'logger_level' => 5, 'logger_param' => getcwd().'/MadelineProto.log'], 'updates' => ['getdifference_interval' => 10], 'serialization' => ['serialization_interval' => 30], 'flood_timeout' => ['wait_if_lt' => 86400]]); $MadelineProto->start(); - -if (!isset($MadelineProto->programmed_call)) { - $MadelineProto->programmed_call = []; -} - -foreach (['my_users', 'times', 'times_messages', 'calls'] as $key) { - if (!isset($MadelineProto->{$key})) { - $MadelineProto->{$key} = []; +foreach (['calls', 'programmed_call', 'my_users'] as $key) { + if (isset($MadelineProto->API->storage[$key])) { + unset($MadelineProto->API->storage[$key]); } } diff --git a/src/danog/MadelineProto/API.php b/src/danog/MadelineProto/API.php index 3bd7b931..a9467f44 100644 --- a/src/danog/MadelineProto/API.php +++ b/src/danog/MadelineProto/API.php @@ -137,10 +137,10 @@ class API extends APIFactory $deferred->resolve(); yield $this->API->initAsync(); $this->APIFactory(); - \danog\MadelineProto\Logger::log('Ping...', Logger::ULTRA_VERBOSE); + //\danog\MadelineProto\Logger::log('Ping...', Logger::ULTRA_VERBOSE); $this->asyncInitPromise = null; - $pong = yield $this->ping(['ping_id' => 3], ['async' => true]); - \danog\MadelineProto\Logger::log('Pong: '.$pong['ping_id'], Logger::ULTRA_VERBOSE); + //$pong = yield $this->ping(['ping_id' => 3], ['async' => true]); + //\danog\MadelineProto\Logger::log('Pong: '.$pong['ping_id'], Logger::ULTRA_VERBOSE); \danog\MadelineProto\Logger::log(\danog\MadelineProto\Lang::$current_lang['madelineproto_ready'], Logger::NOTICE); return; @@ -162,9 +162,9 @@ class API extends APIFactory yield $this->API->initAsync(); $this->APIFactory(); $this->asyncInitPromise = null; - \danog\MadelineProto\Logger::log('Ping...', Logger::ULTRA_VERBOSE); - $pong = yield $this->ping(['ping_id' => 3], ['async' => true]); - \danog\MadelineProto\Logger::log('Pong: '.$pong['ping_id'], Logger::ULTRA_VERBOSE); + //\danog\MadelineProto\Logger::log('Ping...', Logger::ULTRA_VERBOSE); + //$pong = yield $this->ping(['ping_id' => 3], ['async' => true]); + //\danog\MadelineProto\Logger::log('Pong: '.$pong['ping_id'], Logger::ULTRA_VERBOSE); \danog\MadelineProto\Logger::log(\danog\MadelineProto\Lang::$current_lang['madelineproto_ready'], Logger::NOTICE); } diff --git a/src/danog/MadelineProto/Connection.php b/src/danog/MadelineProto/Connection.php index e5a29c28..32e62b46 100644 --- a/src/danog/MadelineProto/Connection.php +++ b/src/danog/MadelineProto/Connection.php @@ -215,13 +215,13 @@ class Connection $this->disconnect(); yield $this->API->datacenter->dcConnectAsync($this->ctx->getDc()); if ($this->API->hasAllAuth() && !$this->hasPendingCalls()) { - $this->callFork((function () { + /*$this->callFork((function () { try { $this->API->method_call_async_read('ping', ['ping_id' => $this->random_int()], ['datacenter' => $this->datacenter]); } catch (\Throwable $e) { $this->API->logger("Got an error while pinging on reconnect: $e", Logger::FATAL_ERROR); } - })()); + })());*/ } } diff --git a/src/danog/MadelineProto/Loop/Update/FeedLoop.php b/src/danog/MadelineProto/Loop/Update/FeedLoop.php index c60c84f3..ded7c3f3 100644 --- a/src/danog/MadelineProto/Loop/Update/FeedLoop.php +++ b/src/danog/MadelineProto/Loop/Update/FeedLoop.php @@ -134,7 +134,6 @@ class FeedLoop extends ResumableSignalLoop $this->state->pts($update['pts']); } - $this->save($update); } } diff --git a/src/danog/MadelineProto/MTProto.php b/src/danog/MadelineProto/MTProto.php index eb9eab94..7203998c 100644 --- a/src/danog/MadelineProto/MTProto.php +++ b/src/danog/MadelineProto/MTProto.php @@ -24,6 +24,7 @@ use danog\MadelineProto\Async\AsyncConstruct; use danog\MadelineProto\Loop\Update\FeedLoop; use danog\MadelineProto\Loop\Update\SeqLoop; use danog\MadelineProto\Loop\Update\UpdateLoop; +use danog\MadelineProto\Loop\Generic\PeriodicLoop; use danog\MadelineProto\MTProtoTools\CombinedUpdatesState; use danog\MadelineProto\MTProtoTools\ReferenceDatabase; use danog\MadelineProto\MTProtoTools\UpdatesState; @@ -149,6 +150,7 @@ class MTProto extends AsyncConstruct implements TLCallback private $supportUser = 0; public $referenceDatabase; public $phoneConfigWatcherId; + private $callCheckerLoop; public $feeders = []; public $updaters = []; public $destructing = false; // Avoid problems with exceptions thrown by forked strands, see tools @@ -208,6 +210,7 @@ class MTProto extends AsyncConstruct implements TLCallback $this->logger->logger(\danog\MadelineProto\Lang::$current_lang['TL_translation'], Logger::ULTRA_VERBOSE); $this->construct_TL($this->settings['tl_schema']['src'], [$this, $this->referenceDatabase]); yield $this->connect_to_all_dcs_async(); + $this->startLoops(); $this->datacenter->curdc = 2; if ((!isset($this->authorization['user']['bot']) || !$this->authorization['user']['bot']) && $this->datacenter->sockets[$this->datacenter->curdc]->temp_auth_key !== null) { try { @@ -292,6 +295,20 @@ class MTProto extends AsyncConstruct implements TLCallback return true; } + public function startLoops() + { + if (!$this->callCheckerLoop) { + $this->callCheckerLoop = new PeriodicLoop($this, [$this, 'checkCalls'], 'call check', 10); + } + $this->callCheckerLoop->start(); + } + public function stopLoops() + { + if ($this->callCheckerLoop) { + $this->callCheckerLoop->signal(true);; + $this->callCheckerLoop = null; + } + } public function __wakeup() { $backtrace = debug_backtrace(0, 3); @@ -469,6 +486,7 @@ class MTProto extends AsyncConstruct implements TLCallback $controller->setMadeline($this); } } + $this->startLoops(); if (yield $this->get_self_async()) { $this->authorized = self::LOGGED_IN; } @@ -490,6 +508,7 @@ class MTProto extends AsyncConstruct implements TLCallback public function __destruct() { + $this->stopLoops(); if ($this->phoneConfigWatcherId) { Loop::cancel($this->phoneConfigWatcherId); } @@ -1143,6 +1162,7 @@ class MTProto extends AsyncConstruct implements TLCallback ); } + public function __debugInfo() { return ['MadelineProto instance '.spl_object_hash($this)]; diff --git a/src/danog/MadelineProto/MTProtoTools/AuthKeyHandler.php b/src/danog/MadelineProto/MTProtoTools/AuthKeyHandler.php index 49af1cba..c32d3bc9 100644 --- a/src/danog/MadelineProto/MTProtoTools/AuthKeyHandler.php +++ b/src/danog/MadelineProto/MTProtoTools/AuthKeyHandler.php @@ -83,7 +83,7 @@ trait AuthKeyHandler * *********************************************************************** * Compute p and q */ - $pq = new \phpseclib\Math\BigInteger((string) $pq_bytes, 256); + $pq = new \phpseclib\Math\BigInteger($pq_bytes, 256); $q = new \phpseclib\Math\BigInteger(0); $p = new \phpseclib\Math\BigInteger(\danog\PrimeModule::auto_single($pq->__toString())); if (!$p->equals(\danog\MadelineProto\Magic::$zero)) { @@ -256,8 +256,8 @@ trait AuthKeyHandler throw new \danog\MadelineProto\SecurityException('wrong server nonce'); } $g = new \phpseclib\Math\BigInteger($server_DH_inner_data['g']); - $g_a = new \phpseclib\Math\BigInteger((string) $server_DH_inner_data['g_a'], 256); - $dh_prime = new \phpseclib\Math\BigInteger((string) $server_DH_inner_data['dh_prime'], 256); + $g_a = new \phpseclib\Math\BigInteger($server_DH_inner_data['g_a'], 256); + $dh_prime = new \phpseclib\Math\BigInteger($server_DH_inner_data['dh_prime'], 256); /* * *********************************************************************** * Time delta diff --git a/src/danog/MadelineProto/MTProtoTools/ResponseHandler.php b/src/danog/MadelineProto/MTProtoTools/ResponseHandler.php index 8b687363..adcfb637 100644 --- a/src/danog/MadelineProto/MTProtoTools/ResponseHandler.php +++ b/src/danog/MadelineProto/MTProtoTools/ResponseHandler.php @@ -605,7 +605,6 @@ trait ResponseHandler if ($actual_updates) { $updates = $actual_updates; } - $this->logger->logger('Parsing updates ('.$updates['_'].') received via the socket...', \danog\MadelineProto\Logger::VERBOSE); switch ($updates['_']) { case 'updates': diff --git a/src/danog/MadelineProto/MTProtoTools/UpdateHandler.php b/src/danog/MadelineProto/MTProtoTools/UpdateHandler.php index 01624695..d99856c3 100644 --- a/src/danog/MadelineProto/MTProtoTools/UpdateHandler.php +++ b/src/danog/MadelineProto/MTProtoTools/UpdateHandler.php @@ -63,11 +63,6 @@ trait UpdateHandler if (!$this->settings['updates']['run_callback']) { $this->settings['updates']['run_callback'] = true; } - array_walk($this->calls, function ($controller, $id) { - if ($controller->getCallState() === \danog\MadelineProto\VoIP::CALL_STATE_ENDED) { - $controller->discard(); - } - }); $params = array_merge(self::DEFAULT_GETUPDATES_PARAMS, $params); diff --git a/src/danog/MadelineProto/RSA.php b/src/danog/MadelineProto/RSA.php index bc08e1c8..f8a12086 100644 --- a/src/danog/MadelineProto/RSA.php +++ b/src/danog/MadelineProto/RSA.php @@ -49,7 +49,7 @@ class RSA { \danog\MadelineProto\Logger::log(\danog\MadelineProto\Lang::$current_lang['rsa_encrypting'], Logger::VERBOSE); - return (new \phpseclib\Math\BigInteger((string) $data, 256))->powMod($this->e, $this->n)->toBytes(); + return (new \phpseclib\Math\BigInteger($data, 256))->powMod($this->e, $this->n)->toBytes(); } /** * Accesses a private variable from an object diff --git a/src/danog/MadelineProto/SecretChats/AuthKeyHandler.php b/src/danog/MadelineProto/SecretChats/AuthKeyHandler.php index 3131f07b..840522a3 100644 --- a/src/danog/MadelineProto/SecretChats/AuthKeyHandler.php +++ b/src/danog/MadelineProto/SecretChats/AuthKeyHandler.php @@ -41,7 +41,7 @@ trait AuthKeyHandler $dh_config = yield $this->get_dh_config_async(); $this->logger->logger('Generating b...', \danog\MadelineProto\Logger::VERBOSE); $b = new \phpseclib\Math\BigInteger($this->random(256), 256); - $params['g_a'] = new \phpseclib\Math\BigInteger((string) $params['g_a'], 256); + $params['g_a'] = new \phpseclib\Math\BigInteger($params['g_a'], 256); $this->check_G($params['g_a'], $dh_config['p']); $key = ['auth_key' => str_pad($params['g_a']->powMod($b, $dh_config['p'])->toBytes(), 256, chr(0), \STR_PAD_LEFT)]; //$this->logger->logger($key); @@ -87,7 +87,7 @@ trait AuthKeyHandler return false; } $dh_config = yield $this->get_dh_config_async(); - $params['g_a_or_b'] = new \phpseclib\Math\BigInteger((string) $params['g_a_or_b'], 256); + $params['g_a_or_b'] = new \phpseclib\Math\BigInteger($params['g_a_or_b'], 256); $this->check_G($params['g_a_or_b'], $dh_config['p']); $key = ['auth_key' => str_pad($params['g_a_or_b']->powMod($this->temp_requested_secret_chats[$params['id']], $dh_config['p'])->toBytes(), 256, chr(0), \STR_PAD_LEFT)]; unset($this->temp_requested_secret_chats[$params['id']]); @@ -139,10 +139,10 @@ trait AuthKeyHandler $my_exchange_id = new \phpseclib\Math\BigInteger($this->secret_chats[$chat]['rekeying'][1], -256); $other_exchange_id = new \phpseclib\Math\BigInteger($params['exchange_id'], -256); //$this->logger->logger($my, $params); - if ($my_exchange_id->compare($other_exchange_id) > 0) { + if ($my_exchange_id > $other_exchange_id) { return; } - if ($my_exchange_id->compare($other_exchange_id) === 0) { + if ($my_exchange_id === $other_exchange_id) { $this->secret_chats[$chat]['rekeying'] = [0]; return; @@ -152,7 +152,7 @@ trait AuthKeyHandler $dh_config = yield $this->get_dh_config_async(); $this->logger->logger('Generating b...', \danog\MadelineProto\Logger::VERBOSE); $b = new \phpseclib\Math\BigInteger($this->random(256), 256); - $params['g_a'] = new \phpseclib\Math\BigInteger((string) $params['g_a'], 256); + $params['g_a'] = new \phpseclib\Math\BigInteger($params['g_a'], 256); $this->check_G($params['g_a'], $dh_config['p']); $key = ['auth_key' => str_pad($params['g_a']->powMod($b, $dh_config['p'])->toBytes(), 256, chr(0), \STR_PAD_LEFT)]; $key['fingerprint'] = substr(sha1($key['auth_key'], true), -8); @@ -175,7 +175,7 @@ trait AuthKeyHandler } $this->logger->logger('Committing rekeying of secret chat '.$chat.'...', \danog\MadelineProto\Logger::VERBOSE); $dh_config = yield $this->get_dh_config_async(); - $params['g_b'] = new \phpseclib\Math\BigInteger((string) $params['g_b'], 256); + $params['g_b'] = new \phpseclib\Math\BigInteger($params['g_b'], 256); $this->check_G($params['g_b'], $dh_config['p']); $key = ['auth_key' => str_pad($params['g_b']->powMod($this->temp_rekeyed_secret_chats[$params['exchange_id']], $dh_config['p'])->toBytes(), 256, chr(0), \STR_PAD_LEFT)]; $key['fingerprint'] = substr(sha1($key['auth_key'], true), -8); diff --git a/src/danog/MadelineProto/Tools.php b/src/danog/MadelineProto/Tools.php index 05daab68..c177ce0d 100644 --- a/src/danog/MadelineProto/Tools.php +++ b/src/danog/MadelineProto/Tools.php @@ -129,7 +129,7 @@ trait Tools throw new TL\Exception(\danog\MadelineProto\Lang::$current_lang['length_not_8']); } - $big = new BigInteger((string) $value, -256); + $big = new BigInteger($value, -256); return (string) $big; } diff --git a/src/danog/MadelineProto/VoIP/AuthKeyHandler.php b/src/danog/MadelineProto/VoIP/AuthKeyHandler.php index ce8cc70b..e733060a 100644 --- a/src/danog/MadelineProto/VoIP/AuthKeyHandler.php +++ b/src/danog/MadelineProto/VoIP/AuthKeyHandler.php @@ -49,11 +49,6 @@ trait AuthKeyHandler if (!class_exists('\\danog\\MadelineProto\\VoIP')) { throw new \danog\MadelineProto\Exception(['extension', 'libtgvoip']); } - array_walk($this->calls, function ($controller, $id) { - if ($controller->getCallState() === \danog\MadelineProto\VoIP::CALL_STATE_ENDED) { - $controller->discard(); - } - }); $user = yield $this->get_info_async($user); if (!isset($user['InputUser']) || $user['InputUser']['_'] === 'inputUserSelf') { throw new \danog\MadelineProto\Exception(\danog\MadelineProto\Lang::$current_lang['peer_not_in_db']); @@ -81,11 +76,6 @@ trait AuthKeyHandler if (!class_exists('\\danog\\MadelineProto\\VoIP')) { throw new \danog\MadelineProto\Exception(); } - array_walk($this->calls, function ($controller, $id) { - if ($controller->getCallState() === \danog\MadelineProto\VoIP::CALL_STATE_ENDED) { - $controller->discard(); - } - }); if ($this->call_status($call['id']) !== \danog\MadelineProto\VoIP::CALL_STATE_ACCEPTED) { $this->logger->logger(sprintf(\danog\MadelineProto\Lang::$current_lang['call_error_1'], $call['id'])); @@ -126,11 +116,6 @@ trait AuthKeyHandler if (!class_exists('\\danog\\MadelineProto\\VoIP')) { throw new \danog\MadelineProto\Exception(['extension', 'libtgvoip']); } - array_walk($this->calls, function ($controller, $id) { - if ($controller->getCallState() === \danog\MadelineProto\VoIP::CALL_STATE_ENDED) { - $controller->discard(); - } - }); if ($this->call_status($params['id']) !== \danog\MadelineProto\VoIP::CALL_STATE_REQUESTED) { $this->logger->logger(sprintf(\danog\MadelineProto\Lang::$current_lang['call_error_2'], $params['id'])); @@ -138,7 +123,7 @@ trait AuthKeyHandler } $this->logger->logger(sprintf(\danog\MadelineProto\Lang::$current_lang['call_confirming'], $this->calls[$params['id']]->getOtherID()), \danog\MadelineProto\Logger::VERBOSE); $dh_config = yield $this->get_dh_config_async(); - $params['g_b'] = new \phpseclib\Math\BigInteger((string) $params['g_b'], 256); + $params['g_b'] = new \phpseclib\Math\BigInteger($params['g_b'], 256); $this->check_G($params['g_b'], $dh_config['p']); $key = str_pad($params['g_b']->powMod($this->calls[$params['id']]->storage['a'], $dh_config['p'])->toBytes(), 256, chr(0), \STR_PAD_LEFT); $res = (yield $this->method_call_async_read('phone.confirmCall', ['key_fingerprint' => substr(sha1($key, true), -8), 'peer' => ['id' => $params['id'], 'access_hash' => $params['access_hash'], '_' => 'inputPhoneCall'], 'g_a' => $this->calls[$params['id']]->storage['g_a'], 'protocol' => ['_' => 'phoneCallProtocol', 'udp_reflector' => true, 'min_layer' => 65, 'max_layer' => \danog\MadelineProto\VoIP::getConnectionMaxLayer()]], ['datacenter' => $this->datacenter->curdc]))['phone_call']; @@ -163,11 +148,6 @@ trait AuthKeyHandler if (!class_exists('\\danog\\MadelineProto\\VoIP')) { throw new \danog\MadelineProto\Exception(['extension', 'libtgvoip']); } - array_walk($this->calls, function ($controller, $id) { - if ($controller->getCallState() === \danog\MadelineProto\VoIP::CALL_STATE_ENDED) { - $controller->discard(); - } - }); if ($this->call_status($params['id']) !== \danog\MadelineProto\VoIP::CALL_STATE_ACCEPTED || !isset($this->calls[$params['id']]->storage['b'])) { $this->logger->logger(sprintf(\danog\MadelineProto\Lang::$current_lang['call_error_3'], $params['id'])); @@ -178,7 +158,7 @@ trait AuthKeyHandler if (hash('sha256', $params['g_a_or_b'], true) != $this->calls[$params['id']]->storage['g_a_hash']) { throw new \danog\MadelineProto\SecurityException(\danog\MadelineProto\Lang::$current_lang['invalid_g_a']); } - $params['g_a_or_b'] = new \phpseclib\Math\BigInteger((string) $params['g_a_or_b'], 256); + $params['g_a_or_b'] = new \phpseclib\Math\BigInteger($params['g_a_or_b'], 256); $this->check_G($params['g_a_or_b'], $dh_config['p']); $key = str_pad($params['g_a_or_b']->powMod($this->calls[$params['id']]->storage['b'], $dh_config['p'])->toBytes(), 256, chr(0), \STR_PAD_LEFT); if (substr(sha1($key, true), -8) != $params['key_fingerprint']) { @@ -204,11 +184,6 @@ trait AuthKeyHandler if (!class_exists('\\danog\\MadelineProto\\VoIP')) { throw new \danog\MadelineProto\Exception(['extension', 'libtgvoip']); } - array_walk($this->calls, function ($controller, $id) { - if ($controller->getCallState() === \danog\MadelineProto\VoIP::CALL_STATE_ENDED) { - $controller->discard(); - } - }); if (isset($this->calls[$id])) { return $this->calls[$id]->getCallState(); } @@ -221,11 +196,6 @@ trait AuthKeyHandler if (!class_exists('\\danog\\MadelineProto\\VoIP')) { throw new \danog\MadelineProto\Exception(['extension', 'libtgvoip']); } - array_walk($this->calls, function ($controller, $id) { - if ($controller->getCallState() === \danog\MadelineProto\VoIP::CALL_STATE_ENDED) { - $controller->discard(); - } - }); return $this->calls[$call]; } @@ -262,6 +232,9 @@ trait AuthKeyHandler in_array($this->settings['updates']['callback'], [['danog\\MadelineProto\\API', 'get_updates_update_handler'], 'get_updates_update_handler']) ? $this->get_updates_update_handler($update) : $this->settings['updates']['callback']($update); } unset($this->calls[$call['id']]); + } + public function checkCalls() + { array_walk($this->calls, function ($controller, $id) { if ($controller->getCallState() === \danog\MadelineProto\VoIP::CALL_STATE_ENDED) { $controller->discard(); diff --git a/src/danog/MadelineProto/Wrappers/Loop.php b/src/danog/MadelineProto/Wrappers/Loop.php index 9755d55e..f10b3a02 100644 --- a/src/danog/MadelineProto/Wrappers/Loop.php +++ b/src/danog/MadelineProto/Wrappers/Loop.php @@ -165,11 +165,6 @@ trait Loop $callback = $this->loop_callback; $callback(); } - array_walk($this->calls, function ($controller, $id) { - if ($controller->getCallState() === \danog\MadelineProto\VoIP::CALL_STATE_ENDED) { - $controller->discard(); - } - }); yield $this->waitUpdate(); } } diff --git a/tests/testing.php b/tests/testing.php index aeaab2c6..dce3c12b 100755 --- a/tests/testing.php +++ b/tests/testing.php @@ -48,7 +48,6 @@ $settings = json_decode(getenv('MTPROTO_SETTINGS'), true) ?: []; */ echo 'Loading MadelineProto...'.PHP_EOL; $MadelineProto = new \danog\MadelineProto\API(getcwd().'/testing.madeline', $settings); - $MadelineProto->fileGetContents('https://google.com'); $MadelineProto->start();