. * * @author Daniil Gentili * @copyright 2016-2023 Daniil Gentili * @license https://opensource.org/licenses/AGPL-3.0 AGPLv3 * @link https://docs.madelineproto.xyz MadelineProto documentation */ namespace danog\MadelineProto\VoIP; use Amp\DeferredFuture; use danog\MadelineProto\Logger; use danog\MadelineProto\Magic; use danog\MadelineProto\MTProtoTools\Crypt; use danog\MadelineProto\PeerNotInDbException; use danog\MadelineProto\VoIP; use danog\MadelineProto\VoIPController; use phpseclib3\Math\BigInteger; use Throwable; use const STR_PAD_LEFT; /** * Manages the creation of the authorization key. * * https://core.telegram.org/mtproto/auth_key * https://core.telegram.org/mtproto/samples-auth_key * * @internal */ trait AuthKeyHandler { /** @var array */ private array $calls = []; private array $pendingCalls = []; /** * Request VoIP call. * * @param mixed $user User */ public function requestCall(mixed $user) { $user = ($this->getInfo($user)); if (!isset($user['InputUser']) || $user['InputUser']['_'] === 'inputUserSelf') { throw new PeerNotInDbException(); } $user = $user['bot_api_id']; if (isset($this->pendingCalls[$user])) { return $this->pendingCalls[$user]->await(); } $deferred = new DeferredFuture; $this->pendingCalls[$user] = $deferred->getFuture(); try { $this->logger->logger(\sprintf('Calling %s...', $user), Logger::VERBOSE); $dh_config = ($this->getDhConfig()); $this->logger->logger('Generating a...', Logger::VERBOSE); $a = BigInteger::randomRange(Magic::$two, $dh_config['p']->subtract(Magic::$two)); $this->logger->logger('Generating g_a...', Logger::VERBOSE); $g_a = $dh_config['g']->powMod($a, $dh_config['p']); Crypt::checkG($g_a, $dh_config['p']); $res = $this->methodCallAsyncRead('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' => 92]])['phone_call']; $res['a'] = $a; $res['g_a'] = \str_pad($g_a->toBytes(), 256, \chr(0), STR_PAD_LEFT); $this->calls[$res['id']] = $controller = new VoIPController($this, $res); unset($this->pendingCalls[$user]); $deferred->complete($controller->public); } catch (Throwable $e) { unset($this->pendingCalls[$user]); $deferred->error($e); } return $deferred->getFuture()->await(); } /** * Get call status. * * @param int $id Call ID */ public function callStatus(int $id): ?CallState { if (isset($this->calls[$id])) { return $this->calls[$id]->getCallState(); } return null; } /** @internal */ public function cleanupCall(int $id): void { unset($this->calls[$id]); } /** * Get call emojis (will return null if the call is not inited yet). * * @internal * * @return ?list{string, string, string, string} */ public function getCallVisualization(int $id): ?array { return ($this->calls[$id] ?? null)?->getVisualization(); } /** * Accept call. */ public function acceptCall(int $id): void { ($this->calls[$id] ?? null)?->accept(); } /** * Discard call. */ public function discardCall(int $id, array $reason = ['_' => 'phoneCallDiscardReasonDisconnect'], array $rating = []): void { ($this->calls[$id] ?? null)?->discard($reason, $rating); } /** * Play file in call. */ public function callPlay(int $id, string $file): void { ($this->calls[$id] ?? null)?->play($file); } /** * Play files on hold in call. * * @param list $files */ public function callPlayOnHold(int $id, array $files): void { ($this->calls[$id] ?? null)?->playOnHold($files); } /** * Get call state. */ public function getCallState(int $id): ?CallState { return ($this->calls[$id] ?? null)?->getCallState(); } }