diff --git a/psalm-baseline.xml b/psalm-baseline.xml index 2a2d3968f..3d06fc804 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -4131,6 +4131,12 @@ + + + + + + diff --git a/src/API.php b/src/API.php index dda71b29a..d4307ecbf 100644 --- a/src/API.php +++ b/src/API.php @@ -51,7 +51,7 @@ final class API extends AbstractAPI * * @var string */ - public const RELEASE = '8.0.0-beta202'; + public const RELEASE = '8.0.0-beta203'; /** * We're not logged in. * diff --git a/src/InternalDoc.php b/src/InternalDoc.php index 9976f197c..879de945f 100644 --- a/src/InternalDoc.php +++ b/src/InternalDoc.php @@ -1081,9 +1081,9 @@ abstract class InternalDoc /** * Provide a stream for a file, URL or amp stream. */ - final public function getStream(\danog\MadelineProto\EventHandler\Message|\danog\MadelineProto\EventHandler\Media|\danog\MadelineProto\LocalFile|\danog\MadelineProto\RemoteUrl|\danog\MadelineProto\BotApiFileId|\Amp\ByteStream\ReadableStream $stream, ?\Amp\Cancellation $cancellation = null): \Amp\ByteStream\ReadableStream + final public function getStream(\danog\MadelineProto\EventHandler\Message|\danog\MadelineProto\EventHandler\Media|\danog\MadelineProto\LocalFile|\danog\MadelineProto\RemoteUrl|\danog\MadelineProto\BotApiFileId|\Amp\ByteStream\ReadableStream $stream, ?\Amp\Cancellation $cancellation = null, ?int &$size = null): \Amp\ByteStream\ReadableStream { - return $this->wrapper->getAPI()->getStream($stream, $cancellation); + return $this->wrapper->getAPI()->getStream($stream, $cancellation, $size); } /** * Obtains a pipe that can be used to upload a file from a stream. diff --git a/src/MTProtoTools/FilesAbstraction.php b/src/MTProtoTools/FilesAbstraction.php index 41ed3ff8c..20fed8b01 100644 --- a/src/MTProtoTools/FilesAbstraction.php +++ b/src/MTProtoTools/FilesAbstraction.php @@ -24,6 +24,8 @@ use Amp\ByteStream\Pipe; use Amp\ByteStream\ReadableBuffer; use Amp\ByteStream\ReadableStream; use Amp\Cancellation; +use Amp\Http\Client\HttpClient; +use Amp\Http\Client\HttpClientBuilder; use Amp\Http\Client\Request; use Amp\Process\Process; use AssertionError; @@ -53,6 +55,7 @@ use Webmozart\Assert\Assert; use function Amp\async; use function Amp\ByteStream\buffer; +use function Amp\File\getSize; use function Amp\File\openFile; use function Amp\Future\await; @@ -65,21 +68,31 @@ use function Amp\Future\await; */ trait FilesAbstraction { + private static ?HttpClient $client; /** * Provide a stream for a file, URL or amp stream. */ - public function getStream(Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream $stream, ?Cancellation $cancellation = null): ReadableStream + public function getStream(Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream $stream, ?Cancellation $cancellation = null, ?int &$size = null): ReadableStream { if ($stream instanceof LocalFile) { + $size = getSize($stream->file); return openFile($stream->file, 'r'); } if ($stream instanceof RemoteUrl) { + self::$client ??= HttpClientBuilder::buildDefault(); $request = new Request($stream->url); $request->setTransferTimeout(INF); - return $this->getHTTPClient()->request( + $request->setBodySizeLimit(512 * 1024 * 8000); + $response = self::$client->request( $request, $cancellation - )->getBody(); + ); + if (($status = $response->getStatus()) !== 200) { + throw new Exception("Wrong status code: {$status} ".$response->getReason()); + } + $size = (int) ($response->getHeader('content-length') ?? $size); + $stream = $response->getBody(); + return $stream; } if ($stream instanceof Message) { $stream = $stream->media; @@ -88,9 +101,11 @@ trait FilesAbstraction } } if ($stream instanceof Media) { + $size = $stream->size; return $stream->getStream(cancellation: $cancellation); } if ($stream instanceof BotApiFileId) { + $size = $stream->size; return $this->downloadToReturnedStream($stream, cancellation: $cancellation); } return $stream; @@ -1127,9 +1142,18 @@ trait FilesAbstraction private function extractMime(bool $secret, Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream &$file, ?string $fileName, ?callable $callback, ?Cancellation $cancellation): string { - $file = $this->getStream($file, $cancellation); + $size = 0; + $file = $this->getStream($file, $cancellation, $size); $p = new Pipe(1024*1024); - $fileFuture = async(fn () => $this->upload(new StreamDuplicator($file, $p->getSink()), $fileName ?? '', $callback, $secret, $cancellation)); + $fileFuture = async(fn () => $this->uploadFromStream( + new StreamDuplicator($file, $p->getSink()), + $size, + 'application/octet-stream', + $fileName ?? '', + $callback, + $secret, + $cancellation + )); $buff = ''; while (\strlen($buff) < 1024*1024 && null !== $chunk = $p->getSource()->read($cancellation)) { @@ -1140,7 +1164,7 @@ trait FilesAbstraction unset($p); $file = $fileFuture->await(); - return (new finfo())->buffer($buff, FILEINFO_MIME_TYPE); + return (new finfo())->buffer($buff, FILEINFO_MIME_TYPE); } private function extractAudioInfo(bool $secret, Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream &$file, ?string $fileName, ?callable $callback, ?Cancellation $cancellation, ?string &$mimeType, array &$attributes, mixed &$thumb): void { @@ -1163,7 +1187,8 @@ trait FilesAbstraction return; } - $file = $this->getStream($file, $cancellation); + $size = 0; + $file = $this->getStream($file, $cancellation, $size); $process = Process::start('ffmpeg -i pipe: -f image2pipe -', cancellation: $cancellation); $stdin = $process->getStdin(); $stdout = $process->getStdout(); @@ -1189,7 +1214,15 @@ trait FilesAbstraction unset($p); } - $fileFuture = async(fn () => $this->upload(new StreamDuplicator($file, ...$streams), $fileName ?? '', $callback, $secret, $cancellation)); + $fileFuture = async(fn () => $this->uploadFromStream( + new StreamDuplicator($file, ...$streams), + $size, + 'application/octet-stream', + $fileName ?? '', + $callback, + $secret, + $cancellation + )); [$stdout, $stderr] = await($f); $process->join($cancellation); @@ -1236,7 +1269,8 @@ trait FilesAbstraction return; } - $file = $this->getStream($file, $cancellation); + $size = 0; + $file = $this->getStream($file, $cancellation, $size); $ffmpeg = 'ffmpeg -i pipe: -ss '.$thumbSeek.' -frames:v 1 -f image2pipe -'; $process = Process::start($ffmpeg, cancellation: $cancellation); $stdin = $process->getStdin(); @@ -1261,7 +1295,15 @@ trait FilesAbstraction unset($p); } - $fileFuture = async(fn () => $this->upload(new StreamDuplicator($file, ...$streams), $fileName ?? '', $callback, $secret, $cancellation)); + $fileFuture = async(fn () => $this->uploadFromStream( + new StreamDuplicator($file, ...$streams), + $size, + 'application/octet-stream', + $fileName ?? '', + $callback, + $secret, + $cancellation + )); [$stdout, $stderr] = await($f); if ($stdout !== '') { diff --git a/src/Tools.php b/src/Tools.php index 071bb21be..5a160a3bd 100644 --- a/src/Tools.php +++ b/src/Tools.php @@ -66,7 +66,6 @@ use const PHP_SAPI; use const STR_PAD_RIGHT; use function Amp\File\openFile; -use function Amp\File\read; use function unpack; /** @@ -712,7 +711,7 @@ abstract class Tools extends AsyncTools } $plugin = is_subclass_of($class, PluginEventHandler::class); $file = (new ReflectionClass($class))->getFileName(); - $code = read($file); + $code = file_get_contents($file); $code = (new ParserFactory)->createForNewestSupportedVersion()->parse($code); Assert::notNull($code); $traverser = new NodeTraverser; diff --git a/tests/testing.php b/tests/testing.php index 1ba07af67..f07101ff6 100755 --- a/tests/testing.php +++ b/tests/testing.php @@ -124,21 +124,6 @@ if (!getenv('GITHUB_SHA') && stripos(($MadelineProto->readline('Do you want to m */ if (!getenv('GITHUB_SHA') && stripos(($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? '); - $offset = 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 - foreach ($updates as $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 - switch ($update['update']['_']) { - case 'updatePhoneCall': - if (is_object($update['update']['phone_call']) && $update['update']['phone_call']->getCallState() === VoIP::CALL_STATE_INCOMING) { - $update['update']['phone_call']->accept()->play('input.raw')->then('input.raw')->playOnHold(['input.raw'])->setOutputFile('output.raw'); - $howmany--; - } - } - } - } } /*