diff --git a/README.md b/README.md index 3f72cf40..ebdbbff5 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ Also note that MadelineProto will perform better if python and a big math extens This project is in beta state. -The MadelineProto API documentation can be found [here (layer 66)](https://daniil.it/MadelineProto/API_docs/). VERY IMPORTANT READ THIS. +The MadelineProto API documentation can be found [here (layer 68)](https://daniil.it/MadelineProto/API_docs/). VERY IMPORTANT READ THIS. Features: diff --git a/src/danog/MadelineProto/API.php b/src/danog/MadelineProto/API.php index 8a381d95..4b1d71b2 100644 --- a/src/danog/MadelineProto/API.php +++ b/src/danog/MadelineProto/API.php @@ -15,7 +15,6 @@ namespace danog\MadelineProto; class API extends APIFactory { use \danog\Serializable; - private $storage = []; public function ___construct($params = []) { @@ -51,7 +50,7 @@ class API extends APIFactory public function __sleep() { - return ['API', 'storage']; + return ['API']; } public function &__get($name) @@ -62,7 +61,7 @@ class API extends APIFactory return $this->API->settings; } - return $this->storage[$name]; + return $this->API->storage[$name]; } public function __set($name, $value) @@ -71,17 +70,17 @@ class API extends APIFactory return $this->API->__construct($value); } - return $this->storage[$name] = $value; + return $this->API->storage[$name] = $value; } public function __isset($name) { - return isset($this->storage[$name]); + return isset($this->API->storage[$name]); } public function __unset($name) { - unset($this->storage[$name]); + unset($this->API->storage[$name]); } public function APIFactory() diff --git a/src/danog/MadelineProto/Lua.php b/src/danog/MadelineProto/Lua.php index a67f6a56..d958bce7 100644 --- a/src/danog/MadelineProto/Lua.php +++ b/src/danog/MadelineProto/Lua.php @@ -80,7 +80,7 @@ class Lua if (is_callable($cb)) { $cb($result, $cb_extra); } - + array_walk_recursive($result, function (&$value, $key) { if ($value instanceof \danog\MadelineProto\TL\Types\Button) $value = $value->jsonSerialize(); }); return $result; } diff --git a/src/danog/MadelineProto/MTProto.php b/src/danog/MadelineProto/MTProto.php index 915ff28c..104d4158 100644 --- a/src/danog/MadelineProto/MTProto.php +++ b/src/danog/MadelineProto/MTProto.php @@ -41,7 +41,6 @@ class MTProto extends \Volatile use \danog\MadelineProto\TL\Conversion\TD; use \danog\MadelineProto\Tools; use \danog\MadelineProto\VoIP\AuthKeyHandler; - use \danog\MadelineProto\VoIP\CallbackHandler; use \danog\MadelineProto\Wrappers\DialogHandler; use \danog\MadelineProto\Wrappers\Login; @@ -110,7 +109,7 @@ class MTProto extends \Volatile const ACCEPTED = 1; const CONFIRMED = 2; const READY = 3; - const EMOJIS = ['😉', '😍', '😛', '😭', '😱', '😡', '😎', '😴', '😵', '😈', '😬', '😇', '😏', '👮', '👷', '💂', '👶', '👨', '👩', '👴', '👵', '😻', '😽', '🙀', '👺', '🙈', '🙉', '🙊', '💀', '👽', '💩', '🔥', '💥', '💤', '👂', '👀', '👃', '👅', '👄', '👍', '👎', '👌', '👊', '✌', '✋', '👐', '👆', '👇', '👉', '👈', '🙏', '👏', '💪', '🚶', '🏃', '💃', '👫', '👪', '👬', '👭', '💅', '🎩', '👑', '👒', '👟', '👞', '👠', '👕', '👗', '👖', '👙', '👜', '👓', '🎀', '💄', '💛', '💙', '💜', '💚', '💍', '💎', '🐶', '🐺', '🐱', '🐭', '🐹', '🐰', '🐸', '🐯', '🐨', '🐻', '🐷', '🐮', '🐗', '🐴', '🐑', '🐘', '🐼', '🐧', '🐥', '🐔', '🐍', '🐢', '🐛', '🐝', '🐜', '🐞', '🐌', '🐙', '🐚', '🐟', '🐬', '🐋', '🐐', '🐊', '🐫', '🍀', '🌹', '🌻', '🍁', '🌾', '🍄', '🌵', '🌴', '🌳', '🌞', '🌚', '🌙', '🌎', '🌋', '⚡', '☔', '❄', '⛄', '🌀', '🌈', '🌊', '🎓', '🎆', '🎃', '👻', '🎅', '🎄', '🎁', '🎈', '🔮', '🎥', '📷', '💿', '💻', '☎', '📡', '📺', '📻', '🔉', '🔔', '⏳', '⏰', '⌚', '🔒', '🔑', '🔎', '💡', '🔦', '🔌', '🔋', '🚿', '🚽', '🔧', '🔨', '🚪', '🚬', '💣', '🔫', '🔪', '💊', '💉', '💰', '💵', '💳', '✉', '📫', '📦', '📅', '📁', '✂', '📌', '📎', '✒', '✏', '📐', '📚', '🔬', '🔭', '🎨', '🎬', '🎤', '🎧', '🎵', '🎹', '🎻', '🎺', '🎸', '👾', '🎮', '🃏', '🎲', '🎯', '🏈', '🏀', '⚽', '⚾', '🎾', '🎱', '🏉', '🎳', '🏁', '🏇', '🏆', '🏊', '🏄', '☕', '🍼', '🍺', '🍷', '🍴', '🍕', '🍔', '🍟', '🍗', '🍱', '🍚', '🍜', '🍡', '🍳', '🍞', '🍩', '🍦', '🎂', '🍰', '🍪', '🍫', '🍭', '🍯', '🍎', '🍏', '🍊', '🍋', '🍒', '🍇', '🍉', '🍓', '🍑', '🍌', '🍐', '🍍', '🍆', '🍅', '🌽', '🏡', '🏥', '🏦', '⛪', '🏰', '⛺', '🏭', '🗻', '🗽', '🎠', '🎡', '⛲', '🎢', '🚢', '🚤', '⚓', '🚀', '✈', '🚁', '🚂', '🚋', '🚎', '🚌', '🚙', '🚗', '🚕', '🚛', '🚨', '🚔', '🚒', '🚑', '🚲', '🚠', '🚜', '🚦', '⚠', '🚧', '⛽', '🎰', '🗿', '🎪', '🎭', '🇯🇵', '🇰🇷', '🇩🇪', '🇨🇳', '🇺🇸', '🇫🇷', '🇪🇸', '🇮🇹', '🇷🇺', '🇬🇧', '1⃣', '2⃣', '3⃣', '4⃣', '5⃣', '6⃣', '7⃣', '8⃣', '9⃣', '0⃣', '🔟', '❗', '❓', '♥', '♦', '💯', '🔗', '🔱', '🔴', '🔵', '🔶', '🔷']; + const JSON_EMOJIS = '["\\ud83d\\ude09","\\ud83d\\ude0d","\\ud83d\\ude1b","\\ud83d\\ude2d","\\ud83d\\ude31","\\ud83d\\ude21","\\ud83d\\ude0e","\\ud83d\\ude34","\\ud83d\\ude35","\\ud83d\\ude08","\\ud83d\\ude2c","\\ud83d\\ude07","\\ud83d\\ude0f","\\ud83d\\udc6e","\\ud83d\\udc77","\\ud83d\\udc82","\\ud83d\\udc76","\\ud83d\\udc68","\\ud83d\\udc69","\\ud83d\\udc74","\\ud83d\\udc75","\\ud83d\\ude3b","\\ud83d\\ude3d","\\ud83d\\ude40","\\ud83d\\udc7a","\\ud83d\\ude48","\\ud83d\\ude49","\\ud83d\\ude4a","\\ud83d\\udc80","\\ud83d\\udc7d","\\ud83d\\udca9","\\ud83d\\udd25","\\ud83d\\udca5","\\ud83d\\udca4","\\ud83d\\udc42","\\ud83d\\udc40","\\ud83d\\udc43","\\ud83d\\udc45","\\ud83d\\udc44","\\ud83d\\udc4d","\\ud83d\\udc4e","\\ud83d\\udc4c","\\ud83d\\udc4a","\\u270c","\\u270b","\\ud83d\\udc50","\\ud83d\\udc46","\\ud83d\\udc47","\\ud83d\\udc49","\\ud83d\\udc48","\\ud83d\\ude4f","\\ud83d\\udc4f","\\ud83d\\udcaa","\\ud83d\\udeb6","\\ud83c\\udfc3","\\ud83d\\udc83","\\ud83d\\udc6b","\\ud83d\\udc6a","\\ud83d\\udc6c","\\ud83d\\udc6d","\\ud83d\\udc85","\\ud83c\\udfa9","\\ud83d\\udc51","\\ud83d\\udc52","\\ud83d\\udc5f","\\ud83d\\udc5e","\\ud83d\\udc60","\\ud83d\\udc55","\\ud83d\\udc57","\\ud83d\\udc56","\\ud83d\\udc59","\\ud83d\\udc5c","\\ud83d\\udc53","\\ud83c\\udf80","\\ud83d\\udc84","\\ud83d\\udc9b","\\ud83d\\udc99","\\ud83d\\udc9c","\\ud83d\\udc9a","\\ud83d\\udc8d","\\ud83d\\udc8e","\\ud83d\\udc36","\\ud83d\\udc3a","\\ud83d\\udc31","\\ud83d\\udc2d","\\ud83d\\udc39","\\ud83d\\udc30","\\ud83d\\udc38","\\ud83d\\udc2f","\\ud83d\\udc28","\\ud83d\\udc3b","\\ud83d\\udc37","\\ud83d\\udc2e","\\ud83d\\udc17","\\ud83d\\udc34","\\ud83d\\udc11","\\ud83d\\udc18","\\ud83d\\udc3c","\\ud83d\\udc27","\\ud83d\\udc25","\\ud83d\\udc14","\\ud83d\\udc0d","\\ud83d\\udc22","\\ud83d\\udc1b","\\ud83d\\udc1d","\\ud83d\\udc1c","\\ud83d\\udc1e","\\ud83d\\udc0c","\\ud83d\\udc19","\\ud83d\\udc1a","\\ud83d\\udc1f","\\ud83d\\udc2c","\\ud83d\\udc0b","\\ud83d\\udc10","\\ud83d\\udc0a","\\ud83d\\udc2b","\\ud83c\\udf40","\\ud83c\\udf39","\\ud83c\\udf3b","\\ud83c\\udf41","\\ud83c\\udf3e","\\ud83c\\udf44","\\ud83c\\udf35","\\ud83c\\udf34","\\ud83c\\udf33","\\ud83c\\udf1e","\\ud83c\\udf1a","\\ud83c\\udf19","\\ud83c\\udf0e","\\ud83c\\udf0b","\\u26a1","\\u2614","\\u2744","\\u26c4","\\ud83c\\udf00","\\ud83c\\udf08","\\ud83c\\udf0a","\\ud83c\\udf93","\\ud83c\\udf86","\\ud83c\\udf83","\\ud83d\\udc7b","\\ud83c\\udf85","\\ud83c\\udf84","\\ud83c\\udf81","\\ud83c\\udf88","\\ud83d\\udd2e","\\ud83c\\udfa5","\\ud83d\\udcf7","\\ud83d\\udcbf","\\ud83d\\udcbb","\\u260e","\\ud83d\\udce1","\\ud83d\\udcfa","\\ud83d\\udcfb","\\ud83d\\udd09","\\ud83d\\udd14","\\u23f3","\\u23f0","\\u231a","\\ud83d\\udd12","\\ud83d\\udd11","\\ud83d\\udd0e","\\ud83d\\udca1","\\ud83d\\udd26","\\ud83d\\udd0c","\\ud83d\\udd0b","\\ud83d\\udebf","\\ud83d\\udebd","\\ud83d\\udd27","\\ud83d\\udd28","\\ud83d\\udeaa","\\ud83d\\udeac","\\ud83d\\udca3","\\ud83d\\udd2b","\\ud83d\\udd2a","\\ud83d\\udc8a","\\ud83d\\udc89","\\ud83d\\udcb0","\\ud83d\\udcb5","\\ud83d\\udcb3","\\u2709","\\ud83d\\udceb","\\ud83d\\udce6","\\ud83d\\udcc5","\\ud83d\\udcc1","\\u2702","\\ud83d\\udccc","\\ud83d\\udcce","\\u2712","\\u270f","\\ud83d\\udcd0","\\ud83d\\udcda","\\ud83d\\udd2c","\\ud83d\\udd2d","\\ud83c\\udfa8","\\ud83c\\udfac","\\ud83c\\udfa4","\\ud83c\\udfa7","\\ud83c\\udfb5","\\ud83c\\udfb9","\\ud83c\\udfbb","\\ud83c\\udfba","\\ud83c\\udfb8","\\ud83d\\udc7e","\\ud83c\\udfae","\\ud83c\\udccf","\\ud83c\\udfb2","\\ud83c\\udfaf","\\ud83c\\udfc8","\\ud83c\\udfc0","\\u26bd","\\u26be","\\ud83c\\udfbe","\\ud83c\\udfb1","\\ud83c\\udfc9","\\ud83c\\udfb3","\\ud83c\\udfc1","\\ud83c\\udfc7","\\ud83c\\udfc6","\\ud83c\\udfca","\\ud83c\\udfc4","\\u2615","\\ud83c\\udf7c","\\ud83c\\udf7a","\\ud83c\\udf77","\\ud83c\\udf74","\\ud83c\\udf55","\\ud83c\\udf54","\\ud83c\\udf5f","\\ud83c\\udf57","\\ud83c\\udf71","\\ud83c\\udf5a","\\ud83c\\udf5c","\\ud83c\\udf61","\\ud83c\\udf73","\\ud83c\\udf5e","\\ud83c\\udf69","\\ud83c\\udf66","\\ud83c\\udf82","\\ud83c\\udf70","\\ud83c\\udf6a","\\ud83c\\udf6b","\\ud83c\\udf6d","\\ud83c\\udf6f","\\ud83c\\udf4e","\\ud83c\\udf4f","\\ud83c\\udf4a","\\ud83c\\udf4b","\\ud83c\\udf52","\\ud83c\\udf47","\\ud83c\\udf49","\\ud83c\\udf53","\\ud83c\\udf51","\\ud83c\\udf4c","\\ud83c\\udf50","\\ud83c\\udf4d","\\ud83c\\udf46","\\ud83c\\udf45","\\ud83c\\udf3d","\\ud83c\\udfe1","\\ud83c\\udfe5","\\ud83c\\udfe6","\\u26ea","\\ud83c\\udff0","\\u26fa","\\ud83c\\udfed","\\ud83d\\uddfb","\\ud83d\\uddfd","\\ud83c\\udfa0","\\ud83c\\udfa1","\\u26f2","\\ud83c\\udfa2","\\ud83d\\udea2","\\ud83d\\udea4","\\u2693","\\ud83d\\ude80","\\u2708","\\ud83d\\ude81","\\ud83d\\ude82","\\ud83d\\ude8b","\\ud83d\\ude8e","\\ud83d\\ude8c","\\ud83d\\ude99","\\ud83d\\ude97","\\ud83d\\ude95","\\ud83d\\ude9b","\\ud83d\\udea8","\\ud83d\\ude94","\\ud83d\\ude92","\\ud83d\\ude91","\\ud83d\\udeb2","\\ud83d\\udea0","\\ud83d\\ude9c","\\ud83d\\udea6","\\u26a0","\\ud83d\\udea7","\\u26fd","\\ud83c\\udfb0","\\ud83d\\uddff","\\ud83c\\udfaa","\\ud83c\\udfad","\\ud83c\\uddef\\ud83c\\uddf5","\\ud83c\\uddf0\\ud83c\\uddf7","\\ud83c\\udde9\\ud83c\\uddea","\\ud83c\\udde8\\ud83c\\uddf3","\\ud83c\\uddfa\\ud83c\\uddf8","\\ud83c\\uddeb\\ud83c\\uddf7","\\ud83c\\uddea\\ud83c\\uddf8","\\ud83c\\uddee\\ud83c\\uddf9","\\ud83c\\uddf7\\ud83c\\uddfa","\\ud83c\\uddec\\ud83c\\udde7","1\\u20e3","2\\u20e3","3\\u20e3","4\\u20e3","5\\u20e3","6\\u20e3","7\\u20e3","8\\u20e3","9\\u20e3","0\\u20e3","\\ud83d\\udd1f","\\u2757","\\u2753","\\u2665","\\u2666","\\ud83d\\udcaf","\\ud83d\\udd17","\\ud83d\\udd31","\\ud83d\\udd34","\\ud83d\\udd35","\\ud83d\\udd36","\\ud83d\\udd37"]'; const TD_PARAMS_CONVERSION = [ 'updateNewMessage' => [ '_' => 'updateNewMessage', @@ -184,9 +183,12 @@ class MTProto extends \Volatile public $run_workers = false; public $threads = false; public $setdem = false; + public $storage = []; + private $emojis; public function ___construct($settings = []) { + $this->emojis = json_decode(self::JSON_EMOJIS); \danog\MadelineProto\Logger::class_exists(); // Detect ipv6 @@ -255,7 +257,7 @@ class MTProto extends \Volatile public function __sleep() { - return ['encrypted_layer', 'settings', 'config', 'authorization', 'authorized', 'rsa_keys', 'last_recv', 'dh_config', 'chats', 'last_stored', 'qres', 'pending_updates', 'updates_state', 'got_state', 'channels_state', 'updates', 'updates_key', 'full_chats', 'msg_ids', 'dialog_params', 'datacenter', 'v', 'constructors', 'td_constructors', 'methods', 'td_methods', 'td_descriptions', 'twoe1984', 'twoe2047', 'twoe2048', 'zero', 'one', 'two', 'three', 'four', 'temp_requested_secret_chats', 'temp_rekeyed_secret_chats', 'secret_chats', 'calls', 'hook_url']; + return ['encrypted_layer', 'settings', 'config', 'authorization', 'authorized', 'rsa_keys', 'last_recv', 'dh_config', 'chats', 'last_stored', 'qres', 'pending_updates', 'updates_state', 'got_state', 'channels_state', 'updates', 'updates_key', 'full_chats', 'msg_ids', 'dialog_params', 'datacenter', 'v', 'constructors', 'td_constructors', 'methods', 'td_methods', 'td_descriptions', 'twoe1984', 'twoe2047', 'twoe2048', 'zero', 'one', 'two', 'three', 'four', 'temp_requested_secret_chats', 'temp_rekeyed_secret_chats', 'secret_chats', 'calls', 'hook_url', 'storage', 'emojis']; } public function __wakeup() @@ -265,6 +267,11 @@ class MTProto extends \Volatile if (\danog\MadelineProto\Logger::$has_thread && is_object(\Thread::getCurrentThread())) { return; } + foreach ($this->calls as $id => $controller) { + if (is_array($controller) || $controller->getCallState() === \danog\MadelineProto\VoIP::CALL_STATE_ENDED) { + unset($this->calls[$id]); + } else var_dump($controller->getCallState()); + } // Detect ipv6 $this->ipv6 = (bool) strlen(@file_get_contents('http://ipv6.test-ipv6.com/', false, stream_context_create(['http' => ['timeout' => 1]]))) > 0; @@ -527,9 +534,6 @@ class MTProto extends \Volatile 'accept_chats' => true, // Should I accept secret chats? Can be true, false or on array of user ids from which to accept chats ], 'calls' => [ - 'accept_calls' => true, // Should I accept calls? Can be true, false or on array of user ids from which to accept calls - 'allow_p2p' => false, // Should I accept p2p calls? - 'incoming_callback' => 'default_incoming_call_callback', 'network_type' => 7, 'log_file_path' => '/tmp/logs.txt', 'stats_dump_file_path' => '/tmp/stats.txt', @@ -743,7 +747,7 @@ class MTProto extends \Volatile public function getV() { - return 52; + return 53; } public function get_self() diff --git a/src/danog/MadelineProto/MTProtoTools/UpdateHandler.php b/src/danog/MadelineProto/MTProtoTools/UpdateHandler.php index fc137735..56c316fe 100644 --- a/src/danog/MadelineProto/MTProtoTools/UpdateHandler.php +++ b/src/danog/MadelineProto/MTProtoTools/UpdateHandler.php @@ -456,6 +456,7 @@ trait UpdateHandler switch ($update['phone_call']['_']) { case 'phoneCallRequested': return $this->accept_call($update['phone_call']); + case 'phoneCallAccepted': $this->confirm_call($update['phone_call']); diff --git a/src/danog/MadelineProto/VoIP/AuthKeyHandler.php b/src/danog/MadelineProto/VoIP/AuthKeyHandler.php index 692755cc..2f8498f4 100644 --- a/src/danog/MadelineProto/VoIP/AuthKeyHandler.php +++ b/src/danog/MadelineProto/VoIP/AuthKeyHandler.php @@ -24,6 +24,12 @@ trait AuthKeyHandler public function request_call($user, $class) { + foreach ($this->calls as $id => $controller) { + if ($controller->getCallState() === \danog\MadelineProto\VoIP::CALL_STATE_ENDED) { + unset($this->calls[$id]); + } + } + $user = $this->get_info($user); if (!isset($user['InputUser'])) { throw new \danog\MadelineProto\Exception('This peer is not present in the internal peer database'); @@ -37,39 +43,40 @@ trait AuthKeyHandler $g_a = $dh_config['g']->powMod($a, $dh_config['p']); $this->check_G($g_a, $dh_config['p']); $res = $this->method_call('phone.requestCall', ['user_id' => $user, 'g_a_hash' => hash('sha256', $g_a->toBytes(), true), 'protocol' => ['_' => 'phoneCallProtocol', 'udp_p2p' => true, 'udp_reflector' => true, 'min_layer' => 65, 'max_layer' => 65]], ['datacenter' => $this->datacenter->curdc]); - $this->calls[$res['phone_call']['id']] = ['status' => self::REQUESTED, 'a' => $a, 'g_a' => $g_a, 'class' => $class]; + $this->calls[$res['phone_call']['id']] = $controller = new \danog\MadelineProto\VoIP(true, time(), $user['user_id'], $res['phone_call']['id'], $this, \danog\MadelineProto\VoIP::CALL_STATE_REQUESTED, $res['phone_call']['protocol']); + $controller->storage = ['a' => $a, 'g_a' => str_pad($g_a->toBytes(), 256, chr(0), \STR_PAD_LEFT)]; + $this->handle_pending_updates(); $this->get_updates_difference(); - - return $res['phone_call']['id']; + return $controller; } public function accept_call($params) { - /* - if ($this->settings['calls']['accept_calls'] === false) { - return false; + foreach ($this->calls as $id => $controller) { + if ($controller->getCallState() === \danog\MadelineProto\VoIP::CALL_STATE_ENDED) { + unset($this->calls[$id]); + } } - if ($this->is_array($this->settings['calls']['accept_calls']) && !$this->in_array($this->settings['calls']['accept_calls'])) { - return false; - } - if ($params['protocol']['udp_p2p'] && !$this->settings['calls']['allow_p2p']) { - return false; - }*/ $dh_config = $this->get_dh_config(); \danog\MadelineProto\Logger::log(['Generating b...'], \danog\MadelineProto\Logger::VERBOSE); $b = \phpseclib\Math\BigInteger::randomRange($this->two, $dh_config['p']->subtract($this->two)); $g_b = $dh_config['g']->powMod($b, $dh_config['p']); $this->check_G($g_b, $dh_config['p']); $res = $this->method_call('phone.acceptCall', ['peer' => ['_' => 'inputPhoneCall', 'id' => $params['id'], 'access_hash' => $params['access_hash']], 'g_b' => $g_b->toBytes(), 'protocol' => ['_' => 'phoneCallProtocol', 'udp_reflector' => true, 'udp_p2p' => true, 'min_layer' => 65, 'max_layer' => 65]], ['datacenter' => $this->datacenter->curdc]); - $this->calls[$res['phone_call']['id']] = ['status' => self::ACCEPTED, 'b' => $b, 'g_a_hash' => $params['g_a_hash']]; + $this->calls[$res['phone_call']['id']] = ['status' => \danog\MadelineProto\VoIP::CALL_STATE_ACCEPTED, 'b' => $b, 'g_a_hash' => $params['g_a_hash']]; $this->handle_pending_updates(); $this->get_updates_difference(); } public function confirm_call($params) { - if ($this->call_status($params['id']) !== self::REQUESTED) { + foreach ($this->calls as $id => $controller) { + if ($controller->getCallState() === \danog\MadelineProto\VoIP::CALL_STATE_ENDED) { + unset($this->calls[$id]); + } + } + if ($this->calls[$params['id']]->getCallState() !== \danog\MadelineProto\VoIP::CALL_STATE_REQUESTED) { \danog\MadelineProto\Logger::log(['Could not find and confirm call '.$params['id']]); return false; @@ -77,33 +84,55 @@ trait AuthKeyHandler $dh_config = $this->get_dh_config(); $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->calls[$params['id']]['a'], $dh_config['p'])->toBytes(), 256, chr(0), \STR_PAD_LEFT)]; + $key = ['auth_key' => str_pad($params['g_b']->powMod($this->calls[$params['id']]->storage['a'], $dh_config['p'])->toBytes(), 256, chr(0), \STR_PAD_LEFT)]; $key['fingerprint'] = substr(sha1($key['auth_key'], true), -8); - $res = $this->method_call('phone.confirmCall', ['key_fingerprint' => $key['fingerprint'], 'peer' => ['id' => $params['id'], 'access_hash' => $params['access_hash'], '_' => 'inputPhoneCall'], 'g_a' => $this->calls[$params['id']]['g_a']->toBytes(), 'protocol' => ['_' => 'phoneCallProtocol', 'udp_reflector' => true, 'min_layer' => 65, 'max_layer' => 65]], ['datacenter' => $this->datacenter->curdc])['phone_call']; + $res = $this->method_call('phone.confirmCall', ['key_fingerprint' => $key['fingerprint'], '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' => 65]], ['datacenter' => $this->datacenter->curdc])['phone_call']; $key['visualization'] = ''; - $length = new \phpseclib\Math\BigInteger(count(self::EMOJIS)); - foreach (str_split(strrev(substr(hash('sha256', $this->calls[$params['id']]['g_a']->toBytes().$key['auth_key'], true), 20)), 8) as $number) { - $key['visualization'] .= self::EMOJIS[(int) ((new \phpseclib\Math\BigInteger($number, -256))->divide($length)[1]->toString())]; + $length = new \phpseclib\Math\BigInteger(count($this->emojis)); + foreach (str_split(hash('sha256', $key['auth_key'].$this->calls[$params['id']]->storage['g_a'], true), 8) as $number) { + var_dump((new \phpseclib\Math\BigInteger($number, -256))->toString()); + var_dump($this->emojis[(int) ((new \phpseclib\Math\BigInteger($number, -256))->divide($length)[1]->toString())]); + $key['visualization'] .= $this->emojis[(int) ((new \phpseclib\Math\BigInteger($number, -256))->divide($length)[1]->toString())]; } - $this->calls[$params['id']] = ['status' => self::READY, 'key' => $key, 'admin' => true, 'user_id' => $params['participant_id'], 'InputPhoneCall' => ['id' => $params['id'], 'access_hash' => $params['access_hash'], '_' => 'inputPhoneCall'], 'in_seq_no_x' => 0, 'out_seq_no_x' => 1, 'layer' => 65, 'updated' => time(), 'incoming' => [], 'outgoing' => [], 'created' => time(), 'protocol' => $params['protocol'], 'controller' => new $this->calls[$params['id']]['class']()]; - $this->calls[$params['id']]['controller']->setConfig($this->config['call_receive_timeout_ms'] / 1000, $this->config['call_connect_timeout_ms'] / 1000, \danog\MadelineProto\VoIP::DATA_SAVING_NEVER, true, true, true, $this->settings['calls']['log_file_path'], $this->settings['calls']['stats_dump_file_path']); - $this->calls[$params['id']]['controller']->setEncryptionKey($key['auth_key'], true); - $this->calls[$params['id']]['controller']->setNetworkType($this->settings['calls']['network_type']); - $this->calls[$params['id']]['controller']->setSharedConfig($this->method_call('phone.getCallConfig', [], ['datacenter' => $this->datacenter->curdc])); - $this->calls[$params['id']]['controller']->setRemoteEndpoints(array_merge([$res['connection']], $res['alternative_connections']), $params['protocol']['udp_p2p']); - $this->calls[$params['id']]['controller']->start(); - $this->calls[$params['id']]['controller']->connect(); - while ($this->calls[$params['id']]['controller']->getState() !== \danog\MadelineProto\VoIP::STATE_ESTABLISHED); - while ($this->calls[$params['id']]['controller']->getOutputState() === \danog\MadelineProto\VoIP::AUDIO_STATE_NONE); + readline(); + $this->calls[$params['id']]->setCallState(\danog\MadelineProto\VoIP::CALL_STATE_READY); + $this->calls[$params['id']]->storage = ['InputPhoneCall' => ['id' => $params['id'], 'access_hash' => $params['access_hash'], '_' => 'inputPhoneCall'], 'protocol' => $params['protocol']]; + $this->calls[$params['id']]->setVisualization($key['visualization']); - $this->calls[$params['id']]['controller']->play('Little Swing.raw')->then('output.raw'); + $this->calls[$params['id']]->configuration = array_merge([ + 'config' => [ + 'recv_timeout' => $this->config['call_receive_timeout_ms'] / 1000, + 'init_timeout' => $this->config['call_connect_timeout_ms'] / 1000, + 'data_saving' => \danog\MadelineProto\VoIP::DATA_SAVING_NEVER, + 'enable_NS' => true, + 'enable_AEC' => true, + 'enable_AGC' => true, + 'log_file_path' => $this->settings['calls']['log_file_path'], + 'stats_dump_file_path' => $this->settings['calls']['stats_dump_file_path'] + ], + 'auth_key' => $key['auth_key'], + 'network_type' => $this->settings['calls']['network_type'], + 'shared_config' => $this->method_call('phone.getCallConfig', [], ['datacenter' => $this->datacenter->curdc]), + 'endpoints' => array_merge([$res['connection']], $res['alternative_connections']), + ], $this->calls[$params['id']]->configuration); + $this->calls[$params['id']]->parseConfig(); + $this->calls[$params['id']]->startTheMagic(); + while ($this->calls[$params['id']]->getState() !== \danog\MadelineProto\VoIP::STATE_ESTABLISHED); + while ($this->calls[$params['id']]->getOutputState() < \danog\MadelineProto\VoIP::AUDIO_STATE_CONFIGURED); + + $this->calls[$params['id']]->play('../Little Swing.raw')->then('output.raw'); $this->handle_pending_updates(); } public function complete_call($params) { - if ($this->call_status($params['id']) !== self::ACCEPTED) { + foreach ($this->calls as $id => $controller) { + if ($controller->getCallState() === \danog\MadelineProto\VoIP::CALL_STATE_ENDED) { + unset($this->calls[$id]); + } + } + if ($this->call_status($params['id']) !== \danog\MadelineProto\VoIP::CALL_STATE_ACCEPTED) { \danog\MadelineProto\Logger::log(['Could not find and confirm call '.$params['id']]); return false; @@ -116,37 +145,40 @@ trait AuthKeyHandler $this->check_G($params['g_a_or_b'], $dh_config['p']); $key = ['auth_key' => str_pad($params['g_a_or_b']->powMod($this->calls[$params['id']]['b'], $dh_config['p'])->toBytes(), 256, chr(0), \STR_PAD_LEFT)]; $key['fingerprint'] = substr(sha1($key['auth_key'], true), -8); -//var_dump($params['key_fingerprint'], $key['fingerprint']); if ($key['fingerprint'] != $params['key_fingerprint']) { // throw new \danog\MadelineProto\SecurityException('Invalid key fingerprint!'); } $key['visualization'] = ''; - $length = new \phpseclib\Math\BigInteger(count(self::EMOJIS)); + $length = new \phpseclib\Math\BigInteger(count($this->emojis)); foreach (str_split(strrev(substr(hash('sha256', $params['g_a_or_b']->toBytes().$key['auth_key'], true), 20)), 8) as $number) { - $key['visualization'] .= self::EMOJIS[(int) ((new \phpseclib\Math\BigInteger($number, -256))->divide($length)[1]->toString())]; + $key['visualization'] .= $this->emojis[(int) ((new \phpseclib\Math\BigInteger($number, -256))->divide($length)[1]->toString())]; } - $this->calls[$params['id']] = ['status' => self::READY, 'key' => $key, 'admin' => false, 'user_id' => $params['admin_id'], 'InputPhoneCall' => ['id' => $params['id'], 'access_hash' => $params['access_hash'], '_' => 'inputPhoneCall'], 'in_seq_no_x' => 1, 'out_seq_no_x' => 0, 'layer' => 65, 'updated' => time(), 'incoming' => [], 'outgoing' => [], 'created' => time(), 'protocol' => $params['protocol'], 'callbacks' => $this->get_incoming_call_callbacks()]; - var_dump('CREATING'); - $this->calls[$params['id']]['controller'] = new \danog\MadelineProto\VoIP($this->calls[$params['id']]['callbacks']['set_state'], $this->calls[$params['id']]['callbacks']['incoming'], $this->calls[$params['id']]['callbacks']['outgoing'], $this, $this->calls[$params['id']]['InputPhoneCall']); - $this->calls[$params['id']]['controller']->setEncryptionKey($key['auth_key'], false); - $this->calls[$params['id']]['controller']->setNetworkType($this->settings['calls']['network_type']); - $this->calls[$params['id']]['controller']->setConfig($this->config['call_receive_timeout_ms'] / 1000, $this->config['call_connect_timeout_ms'] / 1000, \danog\MadelineProto\VoIP::DATA_SAVING_NEVER, true, true, true, $this->settings['calls']['log_file_path'], $this->settings['calls']['stats_dump_file_path']); - $this->calls[$params['id']]['controller']->setSharedConfig($this->method_call('phone.getCallConfig', [], ['datacenter' => $this->datacenter->curdc])); - $this->calls[$params['id']]['controller']->setRemoteEndpoints(array_merge([$params['connection']], $params['alternative_connections']), $params['protocol']['udp_p2p']); - $this->calls[$params['id']]['controller']->start(); - $this->calls[$params['id']]['controller']->connect(); - while ($this->calls[$params['id']]['controller']->getState() !== \danog\MadelineProto\VoIP::STATE_ESTABLISHED); - while ($this->calls[$params['id']]['controller']->getOutputState() === \danog\MadelineProto\VoIP::AUDIO_STATE_NONE); - while ($this->calls[$params['id']]['controller']->getInputState() === \danog\MadelineProto\VoIP::AUDIO_STATE_NONE); + $this->calls[$params['id']] = ['status' => \danog\MadelineProto\VoIP::CALL_STATE_READY, 'key' => $key, 'admin' => false, 'user_id' => $params['admin_id'], 'InputPhoneCall' => ['id' => $params['id'], 'access_hash' => $params['access_hash'], '_' => 'inputPhoneCall'], 'in_seq_no_x' => 1, 'out_seq_no_x' => 0, 'layer' => 65, 'updated' => time(), 'incoming' => [], 'outgoing' => [], 'created' => time(), 'protocol' => $params['protocol'], 'callbacks' => $this->get_incoming_call_callbacks()]; + $this->calls[$params['id']] = new \danog\MadelineProto\VoIP($this->calls[$params['id']]['callbacks']['set_state'], $this->calls[$params['id']]['callbacks']['incoming'], $this->calls[$params['id']]['callbacks']['outgoing'], $this, $this->calls[$params['id']]['InputPhoneCall']); + $this->calls[$params['id']]->setEncryptionKey($key['auth_key'], false); + $this->calls[$params['id']]->setNetworkType($this->settings['calls']['network_type']); + $this->calls[$params['id']]->setConfig($this->config['call_receive_timeout_ms'] / 1000, $this->config['call_connect_timeout_ms'] / 1000, \danog\MadelineProto\VoIP::CALL_STATE_DATA_SAVING_NEVER, true, true, true, $this->settings['calls']['log_file_path'], $this->settings['calls']['stats_dump_file_path']); + $this->calls[$params['id']]->setSharedConfig($this->method_call('phone.getCallConfig', [], ['datacenter' => $this->datacenter->curdc])); + $this->calls[$params['id']]->setRemoteEndpoints(array_merge([$params['connection']], $params['alternative_connections']), $params['protocol']['udp_p2p']); + $this->calls[$params['id']]->start(); + $this->calls[$params['id']]->connect(); + while ($this->calls[$params['id']]->getState() !== \danog\MadelineProto\VoIP::STATE_ESTABLISHED); + while ($this->calls[$params['id']]->getOutputState() === \danog\MadelineProto\VoIP::AUDIO_STATE_NONE); + while ($this->calls[$params['id']]->getInputState() === \danog\MadelineProto\VoIP::AUDIO_STATE_NONE); - $this->calls[$params['id']]['controller']->play('Little Swing.raw')->then('output.raw'); + $this->calls[$params['id']]->play('Little Swing.raw')->then('output.raw'); } public function call_status($id) { + foreach ($this->calls as $id => $controller) { + if ($controller->getCallState() === \danog\MadelineProto\VoIP::CALL_STATE_ENDED) { + unset($this->calls[$id]); + } + } if (isset($this->calls[$id])) { - return $this->calls[$id]['status']; + return $this->calls[$id]->getCallState(); } return -1; @@ -154,18 +186,27 @@ trait AuthKeyHandler public function get_call($call) { + foreach ($this->calls as $id => $controller) { + if ($controller->getCallState() === \danog\MadelineProto\VoIP::CALL_STATE_ENDED) { + unset($this->calls[$id]); + } + } return $this->calls[$call]; } public function discard_call($call) { + foreach ($this->calls as $id => $controller) { + if ($controller->getCallState() === \danog\MadelineProto\VoIP::CALL_STATE_ENDED) { + unset($this->calls[$id]); + } + } \danog\MadelineProto\Logger::log(['Discarding call '.$call.'...'], \danog\MadelineProto\Logger::VERBOSE); - //var_dump(debug_backtrace(0)[0]); if (isset($this->calls[$call])) { - if (isset($this->calls[$call]['InputPhoneCall'])) { + if (isset($this->calls[$call]->storage['InputPhoneCall'])) { try { - $this->method_call('calls.discardCall', ['peer' => $this->calls[$call]['InputPhoneCall']], ['datacenter' => $this->datacenter->curdc]); + $this->method_call('calls.discardCall', ['peer' => $this->calls[$call]->storage['InputPhoneCall']], ['datacenter' => $this->datacenter->curdc]); } catch (\danog\MadelineProto\RPCErrorException $e) { if ($e->rpc !== 'CALL_ALREADY_DECLINED') { throw $e; diff --git a/tests/testing.php b/tests/testing.php index 7963b80d..b0ae84a8 100755 --- a/tests/testing.php +++ b/tests/testing.php @@ -121,8 +121,8 @@ echo 'Serializing MadelineProto to session.madeline...'.PHP_EOL; echo 'Wrote if (stripos(readline('Do you want to make the secret chat tests? (y/n): '), 'y') !== false) { $start = false; - var_dump($id = $MadelineProto->request_call('@danogentili', '\pony')); - while (true) { + $MadelineProto->request_call('@danogentili', '\pony'); + while (1) { $MadelineProto->get_updates(); } $controller = $MadelineProto->get_call($id)['controller'];