From eea186a2361b5bbcbeb3d54c66dd593d36f352e7 Mon Sep 17 00:00:00 2001 From: amir Date: Sun, 24 Mar 2024 22:59:27 +0330 Subject: [PATCH 01/24] Add sendSticker method --- src/InternalDoc.php | 27 +++++++++ src/MTProtoTools/FilesAbstraction.php | 84 ++++++++++++++++++++++++++- 2 files changed, 109 insertions(+), 2 deletions(-) diff --git a/src/InternalDoc.php b/src/InternalDoc.php index d1abfd829..ac8b53ac4 100644 --- a/src/InternalDoc.php +++ b/src/InternalDoc.php @@ -1736,6 +1736,33 @@ abstract class InternalDoc { return $this->wrapper->getAPI()->sendPhoto($peer, $file, $caption, $parseMode, $callback, $fileName, $ttl, $spoiler, $replyToMsgId, $topMsgId, $replyMarkup, $sendAs, $scheduleDate, $silent, $noForwards, $background, $clearDraft, $updateStickersetsOrder, $forceResend, $cancellation); } + /** + * Sends a sticker. + * + * Please use named arguments to call this method. + * + * @param integer|string $peer Destination peer or username. + * @param Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream $file File to upload: can be a message to reuse media present in a message. + * @param ?callable(float, float, int) $callback Upload callback (percent, speed in mpbs, time elapsed) + * @param ?string $fileName Optional file name, if absent will be extracted from the passed $file. + * @param integer|null $replyToMsgId ID of message to reply to. + * @param integer|null $topMsgId ID of thread where to send the message. + * @param array|null $replyMarkup Keyboard information. + * @param integer|null $sendAs Peer to send the message as. + * @param integer|null $scheduleDate Schedule date. + * @param boolean $silent Whether to send the message silently, without triggering notifications. + * @param boolean $noForwards Whether to disable forwards for this message. + * @param boolean $background Send this message as background message + * @param boolean $clearDraft Clears the draft field + * @param boolean $updateStickersetsOrder Whether to move used stickersets to top + * @param boolean $forceResend Whether to forcefully resend the file, even if its type and name are the same. + * @param Cancellation $cancellation Cancellation. + * + */ + final public function sendSticker(string|int $peer, \danog\MadelineProto\EventHandler\Message|\danog\MadelineProto\EventHandler\Media|\danog\MadelineProto\LocalFile|\danog\MadelineProto\RemoteUrl|\danog\MadelineProto\BotApiFileId|\Amp\ByteStream\ReadableStream $file, ?callable $callback = null, ?string $fileName = null, ?int $replyToMsgId = null, ?int $topMsgId = null, ?array $replyMarkup = null, string|int|null $sendAs = null, ?int $scheduleDate = null, bool $silent = false, bool $noForwards = false, bool $background = false, bool $clearDraft = false, bool $updateStickersetsOrder = false, bool $forceResend = false, ?\Amp\Cancellation $cancellation = null): \danog\MadelineProto\EventHandler\Message + { + return $this->wrapper->getAPI()->sendSticker($peer, $file, $callback, $fileName, $replyToMsgId, $topMsgId, $replyMarkup, $sendAs, $scheduleDate, $silent, $noForwards, $background, $clearDraft, $updateStickersetsOrder, $forceResend, $cancellation); + } /** * Set NOOP update handler, ignoring all updates. */ diff --git a/src/MTProtoTools/FilesAbstraction.php b/src/MTProtoTools/FilesAbstraction.php index 566a9a5c8..2a1b51bd1 100644 --- a/src/MTProtoTools/FilesAbstraction.php +++ b/src/MTProtoTools/FilesAbstraction.php @@ -28,6 +28,7 @@ use danog\MadelineProto\BotApiFileId; use danog\MadelineProto\EventHandler\Media; use danog\MadelineProto\EventHandler\Media\Document; use danog\MadelineProto\EventHandler\Media\Photo; +use danog\MadelineProto\EventHandler\Media\Sticker; use danog\MadelineProto\EventHandler\Message; use danog\MadelineProto\Exception; use danog\MadelineProto\LocalFile; @@ -209,7 +210,80 @@ trait FilesAbstraction cancellation: $cancellation ); } + /** + * Sends a sticker. + * + * Please use named arguments to call this method. + * + * @param integer|string $peer Destination peer or username. + * @param Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream $file File to upload: can be a message to reuse media present in a message. + * @param ?callable(float, float, int) $callback Upload callback (percent, speed in mpbs, time elapsed) + * @param ?string $fileName Optional file name, if absent will be extracted from the passed $file. + * @param integer|null $replyToMsgId ID of message to reply to. + * @param integer|null $topMsgId ID of thread where to send the message. + * @param array|null $replyMarkup Keyboard information. + * @param integer|null $sendAs Peer to send the message as. + * @param integer|null $scheduleDate Schedule date. + * @param boolean $silent Whether to send the message silently, without triggering notifications. + * @param boolean $noForwards Whether to disable forwards for this message. + * @param boolean $background Send this message as background message + * @param boolean $clearDraft Clears the draft field + * @param boolean $updateStickersetsOrder Whether to move used stickersets to top + * @param boolean $forceResend Whether to forcefully resend the file, even if its type and name are the same. + * @param Cancellation $cancellation Cancellation. + * + */ + public function sendSticker( + int|string $peer, + Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream $file, + ?callable $callback = null, + ?string $fileName = null, + ?int $replyToMsgId = null, + ?int $topMsgId = null, + ?array $replyMarkup = null, + int|string|null $sendAs = null, + ?int $scheduleDate = null, + bool $silent = false, + bool $noForwards = false, + bool $background = false, + bool $clearDraft = false, + bool $updateStickersetsOrder = false, + bool $forceResend = false, + ?Cancellation $cancellation = null, + ): Message { + if ($file instanceof Message) { + $file = $file->media; + if ($file === null) { + throw new AssertionError("The message must be a media message!"); + } + } + return $this->sendMedia( + type: Sticker::class, + mimeType: 'image/webp', + thumb: null, + peer: $peer, + file: $file, + caption: '', + parseMode: ParseMode::TEXT, + callback: $callback, + fileName: $fileName, + ttl: null, + spoiler: false, + silent: $silent, + background: $background, + clearDraft: $clearDraft, + noForwards: $noForwards, + updateStickersetsOrder: $updateStickersetsOrder, + replyToMsgId: $replyToMsgId, + topMsgId: $topMsgId, + replyMarkup: $replyMarkup, + scheduleDate: $scheduleDate, + sendAs: $sendAs, + forceResend: $forceResend, + cancellation: $cancellation + ); + } /** * Sends a media. * @@ -227,7 +301,7 @@ trait FilesAbstraction ?callable $callback, ?string $fileName, ?int $ttl, - bool $spoiler, + ?bool $spoiler, ?int $replyToMsgId, ?int $topMsgId, ?array $replyMarkup, @@ -407,6 +481,12 @@ trait FilesAbstraction 'file' => $file, 'ttl_seconds' => $ttl, ], + Sticker::class => [ + '_' => 'inputMediaUploadedDocument', + 'file' => $file, + 'mime_type' => $mimeType, + 'attributes' => $attributes, + ], default => [ '_' => 'inputMediaUploadedDocument', 'spoiler' => $spoiler, @@ -421,7 +501,7 @@ trait FilesAbstraction if ($reuseId) { $media['_'] = match ($type) { Photo::class => 'inputMediaPhoto', - Document::class => 'inputMediaDocument', + Sticker::class, Document::class => 'inputMediaDocument', }; $media['id'] = $reuseId; } else { From 0414f2da43fae36d43d0d09ec15229f825ad4ac7 Mon Sep 17 00:00:00 2001 From: amir Date: Mon, 25 Mar 2024 00:09:56 +0330 Subject: [PATCH 02/24] Add sendVideo method --- src/InternalDoc.php | 37 ++++++++ src/MTProtoTools/FilesAbstraction.php | 132 +++++++++++++++++++++++++- 2 files changed, 168 insertions(+), 1 deletion(-) diff --git a/src/InternalDoc.php b/src/InternalDoc.php index ac8b53ac4..aafcf42a2 100644 --- a/src/InternalDoc.php +++ b/src/InternalDoc.php @@ -1763,6 +1763,43 @@ abstract class InternalDoc { return $this->wrapper->getAPI()->sendSticker($peer, $file, $callback, $fileName, $replyToMsgId, $topMsgId, $replyMarkup, $sendAs, $scheduleDate, $silent, $noForwards, $background, $clearDraft, $updateStickersetsOrder, $forceResend, $cancellation); } + /** + * Sends a video. + * + * Please use named arguments to call this method. + * + * @param integer|string $peer Destination peer or username. + * @param Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream $file File to upload: can be a message to reuse media present in a message. + * @param Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream|null $thumb Optional: Thumbnail to upload + * @param string $caption Caption of document + * @param ParseMode $parseMode Text parse mode for the caption + * @param ?callable(float, float, int) $callback Upload callback (percent, speed in mpbs, time elapsed) + * @param ?string $fileName Optional file name, if absent will be extracted from the passed $file. + * @param integer|null $ttl Time to live + * @param boolean $spoiler Whether the message is a spoiler + * @param boolean $roundMessage Whether the message should be round + * @param boolean $supportsStreaming Whether the video supports streaming + * @param boolean $noSound Whether the video has no sound + * @param integer|null $duration Duration of the video + * @param integer|null $width Width of the video + * @param integer|null $height Height of the video + * @param integer|null $replyToMsgId ID of message to reply to. + * @param integer|null $topMsgId ID of thread where to send the message. + * @param array|null $replyMarkup Keyboard information. + * @param integer|string|null $sendAs Peer to send the message as. + * @param integer|null $scheduleDate Schedule date. + * @param boolean $silent Whether to send the message silently, without triggering notifications. + * @param boolean $noForwards Whether to disable forwards for this message. + * @param boolean $background Send this message as background message + * @param boolean $clearDraft Clears the draft field + * @param boolean $forceResend Whether to forcefully resend the file, even if its type and name are the same. + * @param Cancellation $cancellation Cancellation. + * + */ + final public function sendVideo(string|int $peer, \danog\MadelineProto\EventHandler\Message|\danog\MadelineProto\EventHandler\Media|\danog\MadelineProto\LocalFile|\danog\MadelineProto\RemoteUrl|\danog\MadelineProto\BotApiFileId|\Amp\ByteStream\ReadableStream $file, \danog\MadelineProto\EventHandler\Message|\danog\MadelineProto\EventHandler\Media|\danog\MadelineProto\LocalFile|\danog\MadelineProto\RemoteUrl|\danog\MadelineProto\BotApiFileId|\Amp\ByteStream\ReadableStream|null $thumb = null, string $caption = '', \danog\MadelineProto\ParseMode $parseMode = \danog\MadelineProto\ParseMode::TEXT, ?callable $callback = null, ?string $fileName = null, ?int $ttl = null, bool $spoiler = false, bool $roundMessage = false, bool $supportsStreaming = false, bool $noSound = false, ?int $duration = null, ?int $width = null, ?int $height = null, ?int $replyToMsgId = null, ?int $topMsgId = null, ?array $replyMarkup = null, string|int|null $sendAs = null, ?int $scheduleDate = null, bool $silent = false, bool $noForwards = false, bool $background = false, bool $clearDraft = false, bool $forceResend = false, ?\Amp\Cancellation $cancellation = null): \danog\MadelineProto\EventHandler\Message + { + return $this->wrapper->getAPI()->sendVideo($peer, $file, $thumb, $caption, $parseMode, $callback, $fileName, $ttl, $spoiler, $roundMessage, $supportsStreaming, $noSound, $duration, $width, $height, $replyToMsgId, $topMsgId, $replyMarkup, $sendAs, $scheduleDate, $silent, $noForwards, $background, $clearDraft, $forceResend, $cancellation); + } /** * Set NOOP update handler, ignoring all updates. */ diff --git a/src/MTProtoTools/FilesAbstraction.php b/src/MTProtoTools/FilesAbstraction.php index 2a1b51bd1..8af4b0924 100644 --- a/src/MTProtoTools/FilesAbstraction.php +++ b/src/MTProtoTools/FilesAbstraction.php @@ -29,6 +29,7 @@ use danog\MadelineProto\EventHandler\Media; use danog\MadelineProto\EventHandler\Media\Document; use danog\MadelineProto\EventHandler\Media\Photo; use danog\MadelineProto\EventHandler\Media\Sticker; +use danog\MadelineProto\EventHandler\Media\Video; use danog\MadelineProto\EventHandler\Message; use danog\MadelineProto\Exception; use danog\MadelineProto\LocalFile; @@ -109,6 +110,7 @@ trait FilesAbstraction type: Document::class, mimeType: $mimeType, thumb: $thumb, + attributes: [], peer: $peer, file: $file, caption: $caption, @@ -188,6 +190,7 @@ trait FilesAbstraction type: Photo::class, mimeType: 'image/jpeg', thumb: null, + attributes: [], peer: $peer, file: $file, caption: $caption, @@ -262,6 +265,7 @@ trait FilesAbstraction type: Sticker::class, mimeType: 'image/webp', thumb: null, + attributes: [], peer: $peer, file: $file, caption: '', @@ -284,6 +288,110 @@ trait FilesAbstraction cancellation: $cancellation ); } + /** + * Sends a video. + * + * Please use named arguments to call this method. + * + * @param integer|string $peer Destination peer or username. + * @param Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream $file File to upload: can be a message to reuse media present in a message. + * @param Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream|null $thumb Optional: Thumbnail to upload + * @param string $caption Caption of document + * @param ParseMode $parseMode Text parse mode for the caption + * @param ?callable(float, float, int) $callback Upload callback (percent, speed in mpbs, time elapsed) + * @param ?string $fileName Optional file name, if absent will be extracted from the passed $file. + * @param integer|null $ttl Time to live + * @param boolean $spoiler Whether the message is a spoiler + * @param boolean $roundMessage Whether the message should be round + * @param boolean $supportsStreaming Whether the video supports streaming + * @param boolean $noSound Whether the video has no sound + * @param integer|null $duration Duration of the video + * @param integer|null $width Width of the video + * @param integer|null $height Height of the video + * @param integer|null $replyToMsgId ID of message to reply to. + * @param integer|null $topMsgId ID of thread where to send the message. + * @param array|null $replyMarkup Keyboard information. + * @param integer|string|null $sendAs Peer to send the message as. + * @param integer|null $scheduleDate Schedule date. + * @param boolean $silent Whether to send the message silently, without triggering notifications. + * @param boolean $noForwards Whether to disable forwards for this message. + * @param boolean $background Send this message as background message + * @param boolean $clearDraft Clears the draft field + * @param boolean $forceResend Whether to forcefully resend the file, even if its type and name are the same. + * @param Cancellation $cancellation Cancellation. + * + */ + public function sendVideo( + int|string $peer, + Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream $file, + Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream|null $thumb = null, + string $caption = '', + ParseMode $parseMode = ParseMode::TEXT, + ?callable $callback = null, + ?string $fileName = null, + ?int $ttl = null, + bool $spoiler = false, + bool $roundMessage = false, + bool $supportsStreaming = true, + bool $noSound = false, + ?int $duration = null, + ?int $width = null, + ?int $height = null, + ?int $replyToMsgId = null, + ?int $topMsgId = null, + ?array $replyMarkup = null, + int|string|null $sendAs = null, + ?int $scheduleDate = null, + bool $silent = false, + bool $noForwards = false, + bool $background = false, + bool $clearDraft = false, + bool $forceResend = false, + ?Cancellation $cancellation = null, + ): Message { + if ($file instanceof Message) { + $file = $file->media; + if ($file === null) { + throw new AssertionError("The message must be a media message!"); + } + } + + $attributes = [ + 'round_message' => $roundMessage, + 'supports_streaming' => $supportsStreaming, + 'no_sound' => $noSound, + 'duration' => $duration, + 'w' => $width, + 'h' => $height, + ]; + + return $this->sendMedia( + type: Video::class, + mimeType: 'video/mp4', + thumb: $thumb, + attributes: $attributes, + peer: $peer, + file: $file, + caption: $caption, + parseMode: $parseMode, + callback: $callback, + fileName: $fileName, + ttl: $ttl, + spoiler: $spoiler, + silent: $silent, + background: $background, + clearDraft: $clearDraft, + noForwards: $noForwards, + updateStickersetsOrder: false, + replyToMsgId: $replyToMsgId, + topMsgId: $topMsgId, + replyMarkup: $replyMarkup, + scheduleDate: $scheduleDate, + sendAs: $sendAs, + forceResend: $forceResend, + cancellation: $cancellation + ); + } /** * Sends a media. * @@ -296,6 +404,7 @@ trait FilesAbstraction ?string $mimeType, Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream $file, Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream|null $thumb, + array $attributes, string $caption, ParseMode $parseMode, ?callable $callback, @@ -352,6 +461,17 @@ trait FilesAbstraction } $attributes = match ($type) { + Video::class => [ + [ + '_' => 'documentAttributeVideo', + 'round_message' => $file->roundMessage ?? $attributes['round_message'], + 'supports_streaming' => $file->supportsStreaming ?? $attributes['supports_streaming'], + 'no_sound' => $file->noSound ?? $attributes['no_sound'], + 'duration' => $file->duration ?? $attributes['duration'], + 'w' => $file->width ?? $attributes['w'], + 'h' => $file->height ?? $attributes['h'], + ], + ], default => [], }; $attributes[] = ['_' => 'documentAttributeFilename', 'file_name' => $fileName]; @@ -487,6 +607,16 @@ trait FilesAbstraction 'mime_type' => $mimeType, 'attributes' => $attributes, ], + Video::class => [ + '_' => 'inputMediaUploadedDocument', + 'spoiler' => $spoiler, + 'ttl_seconds' => $ttl, + 'force_file' => false, + 'file' => $file, + 'thumb' => $thumb, + 'mime_type' => $mimeType, + 'attributes' => $attributes, + ], default => [ '_' => 'inputMediaUploadedDocument', 'spoiler' => $spoiler, @@ -501,7 +631,7 @@ trait FilesAbstraction if ($reuseId) { $media['_'] = match ($type) { Photo::class => 'inputMediaPhoto', - Sticker::class, Document::class => 'inputMediaDocument', + Sticker::class, Document::class, Video::class => 'inputMediaDocument', }; $media['id'] = $reuseId; } else { From ec15ca4084e47b7179cbdc554c87f129256f172e Mon Sep 17 00:00:00 2001 From: amir Date: Mon, 25 Mar 2024 22:19:47 +0330 Subject: [PATCH 03/24] Extract video data using ffmpeg --- src/MTProtoTools/FilesAbstraction.php | 32 +++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/MTProtoTools/FilesAbstraction.php b/src/MTProtoTools/FilesAbstraction.php index 8af4b0924..a290306c2 100644 --- a/src/MTProtoTools/FilesAbstraction.php +++ b/src/MTProtoTools/FilesAbstraction.php @@ -23,6 +23,7 @@ namespace danog\MadelineProto\MTProtoTools; use Amp\ByteStream\ReadableBuffer; use Amp\ByteStream\ReadableStream; use Amp\Cancellation; +use Amp\Process\Process; use AssertionError; use danog\MadelineProto\BotApiFileId; use danog\MadelineProto\EventHandler\Media; @@ -40,6 +41,7 @@ use danog\MadelineProto\TL\Types\Bytes; use Webmozart\Assert\Assert; use function Amp\ByteStream\buffer; +use function Amp\ByteStream\pipe; /** * Manages upload and download of files. @@ -593,6 +595,35 @@ trait FilesAbstraction 'cancellation' => $cancellation, ]; } else { + + if ($type === Video::class) { + $file = $this->getStream($file, $cancellation); + if ($thumb === null) { + $ffmpeg = 'ffmpeg -i pipe: -ss 00:00:01.000 -frames:v 1 -f image2pipe -vcodec mjpeg pipe:1'; + $process = Process::start($ffmpeg); + pipe($file, $process->getStdin()); + $thumb = buffer($process->getStdout()); + $thumb = new ReadableBuffer($thumb); + } + if ($attributes[0]['duration'] === null || $attributes[0]['w'] === null || $attributes[0]['h'] === null) { + $ffmpeg = 'ffmpeg -i pipe: 2>&1'; + $process = Process::start($ffmpeg); + pipe($file, $process->getStdin()); + $output = buffer($process->getStdout()); + if (preg_match('/(\d{3,4})x(\d{3,4})/', $output, $whMatch) and preg_match('/Duration: (\d{2}):(\d{2}):(\d{2})/', $output, $dMatch)) { + $width = $whMatch[1]; + $height = $whMatch[2]; + $hours = (int) $dMatch[1]; + $minutes = (int) $dMatch[2]; + $seconds = (int) $dMatch[3]; + $duration = $hours * 3600 + $minutes * 60 + $seconds; + $attributes[0]['w'] ??= $width; + $attributes[0]['h'] ??= $height; + $attributes[0]['duration'] ??= $duration; + } + } + } + $method = 'messages.sendMedia'; $media = match ($type) { Photo::class => [ @@ -609,6 +640,7 @@ trait FilesAbstraction ], Video::class => [ '_' => 'inputMediaUploadedDocument', + 'nosound_video' => $attributes[0]['no_sound'], 'spoiler' => $spoiler, 'ttl_seconds' => $ttl, 'force_file' => false, From b959240788cf57ef8deaae9312ea0d75e29b3b28 Mon Sep 17 00:00:00 2001 From: amir Date: Mon, 25 Mar 2024 22:28:49 +0330 Subject: [PATCH 04/24] Fixup --- src/MTProtoTools/FilesAbstraction.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/MTProtoTools/FilesAbstraction.php b/src/MTProtoTools/FilesAbstraction.php index a290306c2..39363745b 100644 --- a/src/MTProtoTools/FilesAbstraction.php +++ b/src/MTProtoTools/FilesAbstraction.php @@ -40,7 +40,9 @@ use danog\MadelineProto\Settings; use danog\MadelineProto\TL\Types\Bytes; use Webmozart\Assert\Assert; +use function Amp\async; use function Amp\ByteStream\buffer; +use function Amp\ByteStream\getStderr; use function Amp\ByteStream\pipe; /** @@ -601,14 +603,14 @@ trait FilesAbstraction if ($thumb === null) { $ffmpeg = 'ffmpeg -i pipe: -ss 00:00:01.000 -frames:v 1 -f image2pipe -vcodec mjpeg pipe:1'; $process = Process::start($ffmpeg); - pipe($file, $process->getStdin()); + async(fn () => pipe($file, $process->getStdin()))->finally(fn () => $process->getStdin()->close()); $thumb = buffer($process->getStdout()); $thumb = new ReadableBuffer($thumb); } if ($attributes[0]['duration'] === null || $attributes[0]['w'] === null || $attributes[0]['h'] === null) { $ffmpeg = 'ffmpeg -i pipe: 2>&1'; $process = Process::start($ffmpeg); - pipe($file, $process->getStdin()); + async(fn () => pipe($file, $process->getStdin()))->finally(fn () => $process->getStdin()->close()); $output = buffer($process->getStdout()); if (preg_match('/(\d{3,4})x(\d{3,4})/', $output, $whMatch) and preg_match('/Duration: (\d{2}):(\d{2}):(\d{2})/', $output, $dMatch)) { $width = $whMatch[1]; From 5ab6643cd4e0f1c3a43b3bdcf0000ccda1d5cc47 Mon Sep 17 00:00:00 2001 From: amir Date: Tue, 26 Mar 2024 02:44:31 +0330 Subject: [PATCH 05/24] Make the extraction a single command --- src/MTProtoTools/FilesAbstraction.php | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/src/MTProtoTools/FilesAbstraction.php b/src/MTProtoTools/FilesAbstraction.php index 39363745b..1e28c5f58 100644 --- a/src/MTProtoTools/FilesAbstraction.php +++ b/src/MTProtoTools/FilesAbstraction.php @@ -599,26 +599,21 @@ trait FilesAbstraction } else { if ($type === Video::class) { - $file = $this->getStream($file, $cancellation); - if ($thumb === null) { + if ($thumb === null || $attributes[0]['duration'] === null || $attributes[0]['w'] === null || $attributes[0]['h'] === null) { + $file = $this->getStream($file, $cancellation); $ffmpeg = 'ffmpeg -i pipe: -ss 00:00:01.000 -frames:v 1 -f image2pipe -vcodec mjpeg pipe:1'; $process = Process::start($ffmpeg); async(fn () => pipe($file, $process->getStdin()))->finally(fn () => $process->getStdin()->close()); - $thumb = buffer($process->getStdout()); - $thumb = new ReadableBuffer($thumb); - } - if ($attributes[0]['duration'] === null || $attributes[0]['w'] === null || $attributes[0]['h'] === null) { - $ffmpeg = 'ffmpeg -i pipe: 2>&1'; - $process = Process::start($ffmpeg); - async(fn () => pipe($file, $process->getStdin()))->finally(fn () => $process->getStdin()->close()); - $output = buffer($process->getStdout()); - if (preg_match('/(\d{3,4})x(\d{3,4})/', $output, $whMatch) and preg_match('/Duration: (\d{2}):(\d{2}):(\d{2})/', $output, $dMatch)) { - $width = $whMatch[1]; - $height = $whMatch[2]; - $hours = (int) $dMatch[1]; - $minutes = (int) $dMatch[2]; - $seconds = (int) $dMatch[3]; + $thumb ??= new ReadableBuffer(buffer($process->getStdout())); + $output = buffer($process->getStderr()); + if (preg_match('~Duration: (\d{2}:\d{2}:\d{2}\.\d{2}),.*? (\d{3,4})x(\d{3,4})~s', $output, $matches)) { + $time = explode(':', $matches[1]); + $hours = (int) $time[0]; + $minutes = (int) $time[1]; + $seconds = (int) $time[2]; $duration = $hours * 3600 + $minutes * 60 + $seconds; + $width = $matches[2]; + $height = $matches[3]; $attributes[0]['w'] ??= $width; $attributes[0]['h'] ??= $height; $attributes[0]['duration'] ??= $duration; From 5e384ca89de4051608c2f3dc83c6ab7fd481394c Mon Sep 17 00:00:00 2001 From: amir Date: Tue, 26 Mar 2024 03:10:29 +0330 Subject: [PATCH 06/24] Add some logs --- src/MTProtoTools/FilesAbstraction.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/MTProtoTools/FilesAbstraction.php b/src/MTProtoTools/FilesAbstraction.php index 1e28c5f58..eb4ea144c 100644 --- a/src/MTProtoTools/FilesAbstraction.php +++ b/src/MTProtoTools/FilesAbstraction.php @@ -599,7 +599,9 @@ trait FilesAbstraction } else { if ($type === Video::class) { - if ($thumb === null || $attributes[0]['duration'] === null || $attributes[0]['w'] === null || $attributes[0]['h'] === null) { + if (Process::start('ffmpeg -version')->join() !== 0) { + $this->logger->logger('Install ffmpeg for video info extraction!'); + } elseif ($thumb === null || $attributes[0]['duration'] === null || $attributes[0]['w'] === null || $attributes[0]['h'] === null) { $file = $this->getStream($file, $cancellation); $ffmpeg = 'ffmpeg -i pipe: -ss 00:00:01.000 -frames:v 1 -f image2pipe -vcodec mjpeg pipe:1'; $process = Process::start($ffmpeg); From adf0ea7fe017d36159b753f34f78f11c9fef422c Mon Sep 17 00:00:00 2001 From: amir Date: Tue, 26 Mar 2024 04:43:59 +0330 Subject: [PATCH 07/24] Set the sticker to the standard size --- src/MTProtoTools/FilesAbstraction.php | 32 ++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/src/MTProtoTools/FilesAbstraction.php b/src/MTProtoTools/FilesAbstraction.php index eb4ea144c..dbaa51a5e 100644 --- a/src/MTProtoTools/FilesAbstraction.php +++ b/src/MTProtoTools/FilesAbstraction.php @@ -598,7 +598,9 @@ trait FilesAbstraction ]; } else { - if ($type === Video::class) { + if ($reuseId) { + // Reuse + } elseif ($type === Video::class) { if (Process::start('ffmpeg -version')->join() !== 0) { $this->logger->logger('Install ffmpeg for video info extraction!'); } elseif ($thumb === null || $attributes[0]['duration'] === null || $attributes[0]['w'] === null || $attributes[0]['h'] === null) { @@ -621,6 +623,34 @@ trait FilesAbstraction $attributes[0]['duration'] ??= $duration; } } + } elseif ($type === Sticker::class) { + if (!extension_loaded('gd')) { + throw Exception::extension('gd'); + } + $file = buffer($this->getStream($file, $cancellation), $cancellation); + $img = imagecreatefromstring($file); + $width = imagesx($img); + $height = imagesy($img); + if ($width > $height) { + $newWidth = 512; + $newHeight = (int) (512 * $height / $width); + } elseif ($width < $height) { + $newWidth = (int) (512 * $width / $height); + $newHeight = 512; + } else { + $newWidth = 512; + $newHeight = 512; + } + $temp = imagecreatetruecolor($newWidth, $newHeight); + imagecopyresized($temp, $img, 0, 0, 0, 0, $newWidth, $newHeight, $width, $height); + $stream = fopen('php://memory', 'r+'); + imagewebp($temp, $stream); + rewind($stream); + $file = stream_get_contents($stream); + fclose($stream); + unset($stream); + unset($temp); + $file = new ReadableBuffer($file); } $method = 'messages.sendMedia'; From ad4fc18d06b1a34d71f59c1dfe83faa9a942331b Mon Sep 17 00:00:00 2001 From: amir Date: Tue, 26 Mar 2024 16:16:28 +0330 Subject: [PATCH 08/24] Add sendAudio method --- src/InternalDoc.php | 32 +++++++ src/MTProtoTools/FilesAbstraction.php | 121 +++++++++++++++++++++++++- 2 files changed, 152 insertions(+), 1 deletion(-) diff --git a/src/InternalDoc.php b/src/InternalDoc.php index aafcf42a2..6687c3177 100644 --- a/src/InternalDoc.php +++ b/src/InternalDoc.php @@ -1800,6 +1800,38 @@ abstract class InternalDoc { return $this->wrapper->getAPI()->sendVideo($peer, $file, $thumb, $caption, $parseMode, $callback, $fileName, $ttl, $spoiler, $roundMessage, $supportsStreaming, $noSound, $duration, $width, $height, $replyToMsgId, $topMsgId, $replyMarkup, $sendAs, $scheduleDate, $silent, $noForwards, $background, $clearDraft, $forceResend, $cancellation); } + /** + * Sends an audio. + * + * Please use named arguments to call this method. + * + * @param integer|string $peer Destination peer or username. + * @param Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream $file File to upload: can be a message to reuse media present in a message. + * @param Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream|null $thumb Optional: Thumbnail to upload + * @param string $caption Caption of document + * @param ParseMode $parseMode Text parse mode for the caption + * @param ?callable(float, float, int) $callback Upload callback (percent, speed in mpbs, time elapsed) + * @param ?string $fileName Optional file name, if absent will be extracted from the passed $file. + * @param integer|null $duration Duration of the audio + * @param string|null $title Title of the audio + * @param string|null $performer Performer of the audio + * @param integer|null $replyToMsgId ID of message to reply to. + * @param integer|null $topMsgId ID of thread where to send the message. + * @param array|null $replyMarkup Keyboard information. + * @param integer|string|null $sendAs Peer to send the message as. + * @param integer|null $scheduleDate Schedule date. + * @param boolean $silent Whether to send the message silently, without triggering notifications. + * @param boolean $noForwards Whether to disable forwards for this message. + * @param boolean $background Send this message as background message + * @param boolean $clearDraft Clears the draft field + * @param boolean $forceResend Whether to forcefully resend the file, even if its type and name are the same. + * @param ?Cancellation $cancellation Cancellation. + * + */ + final public function sendAudio(string|int $peer, \danog\MadelineProto\EventHandler\Message|\danog\MadelineProto\EventHandler\Media|\danog\MadelineProto\LocalFile|\danog\MadelineProto\RemoteUrl|\danog\MadelineProto\BotApiFileId|\Amp\ByteStream\ReadableStream $file, \danog\MadelineProto\EventHandler\Message|\danog\MadelineProto\EventHandler\Media|\danog\MadelineProto\LocalFile|\danog\MadelineProto\RemoteUrl|\danog\MadelineProto\BotApiFileId|\Amp\ByteStream\ReadableStream|null $thumb = null, string $caption = '', \danog\MadelineProto\ParseMode $parseMode = \danog\MadelineProto\ParseMode::TEXT, ?callable $callback = null, ?string $fileName = null, ?int $duration = null, ?string $title = null, ?string $performer = null, ?int $replyToMsgId = null, ?int $topMsgId = null, ?array $replyMarkup = null, string|int|null $sendAs = null, ?int $scheduleDate = null, bool $silent = false, bool $noForwards = false, bool $background = false, bool $clearDraft = false, bool $forceResend = false, ?\Amp\Cancellation $cancellation = null): \danog\MadelineProto\EventHandler\Message + { + return $this->wrapper->getAPI()->sendAudio($peer, $file, $thumb, $caption, $parseMode, $callback, $fileName, $duration, $title, $performer, $replyToMsgId, $topMsgId, $replyMarkup, $sendAs, $scheduleDate, $silent, $noForwards, $background, $clearDraft, $forceResend, $cancellation); + } /** * Set NOOP update handler, ignoring all updates. */ diff --git a/src/MTProtoTools/FilesAbstraction.php b/src/MTProtoTools/FilesAbstraction.php index dbaa51a5e..59d12bc28 100644 --- a/src/MTProtoTools/FilesAbstraction.php +++ b/src/MTProtoTools/FilesAbstraction.php @@ -27,6 +27,7 @@ use Amp\Process\Process; use AssertionError; use danog\MadelineProto\BotApiFileId; use danog\MadelineProto\EventHandler\Media; +use danog\MadelineProto\EventHandler\Media\Audio; use danog\MadelineProto\EventHandler\Media\Document; use danog\MadelineProto\EventHandler\Media\Photo; use danog\MadelineProto\EventHandler\Media\Sticker; @@ -396,6 +397,91 @@ trait FilesAbstraction cancellation: $cancellation ); } + /** + * Sends an audio. + * + * Please use named arguments to call this method. + * + * @param integer|string $peer Destination peer or username. + * @param Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream $file File to upload: can be a message to reuse media present in a message. + * @param Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream|null $thumb Optional: Thumbnail to upload + * @param string $caption Caption of document + * @param ParseMode $parseMode Text parse mode for the caption + * @param ?callable(float, float, int) $callback Upload callback (percent, speed in mpbs, time elapsed) + * @param ?string $fileName Optional file name, if absent will be extracted from the passed $file. + * @param integer|null $duration Duration of the audio + * @param string|null $title Title of the audio + * @param string|null $performer Performer of the audio + * @param integer|null $replyToMsgId ID of message to reply to. + * @param integer|null $topMsgId ID of thread where to send the message. + * @param array|null $replyMarkup Keyboard information. + * @param integer|string|null $sendAs Peer to send the message as. + * @param integer|null $scheduleDate Schedule date. + * @param boolean $silent Whether to send the message silently, without triggering notifications. + * @param boolean $noForwards Whether to disable forwards for this message. + * @param boolean $background Send this message as background message + * @param boolean $clearDraft Clears the draft field + * @param boolean $forceResend Whether to forcefully resend the file, even if its type and name are the same. + * @param ?Cancellation $cancellation Cancellation. + * + */ + public function sendAudio( + int|string $peer, + Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream $file, + Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream|null $thumb = null, + string $caption = '', + ParseMode $parseMode = ParseMode::TEXT, + ?callable $callback = null, + ?string $fileName = null, + ?int $duration = null, + ?string $title = null, + ?string $performer = null, + ?int $replyToMsgId = null, + ?int $topMsgId = null, + ?array $replyMarkup = null, + int|string|null $sendAs = null, + ?int $scheduleDate = null, + bool $silent = false, + bool $noForwards = false, + bool $background = false, + bool $clearDraft = false, + bool $forceResend = false, + ?Cancellation $cancellation = null, + ): Message { + $attributes = [ + 'duration' => $duration, + 'title' => $title, + 'performer' => $performer, + ]; + + return $this->sendMedia( + type: Audio::class, + mimeType: 'audio/mpeg', + thumb: $thumb, + attributes: $attributes, + peer: $peer, + file: $file, + caption: $caption, + parseMode: $parseMode, + callback: $callback, + fileName: $fileName, + ttl: null, + spoiler: null, + silent: $silent, + background: $background, + clearDraft: $clearDraft, + noForwards: $noForwards, + updateStickersetsOrder: false, + replyToMsgId: $replyToMsgId, + topMsgId: $topMsgId, + replyMarkup: $replyMarkup, + scheduleDate: $scheduleDate, + sendAs: $sendAs, + forceResend: $forceResend, + cancellation: $cancellation + ); + } + /** * Sends a media. * @@ -476,6 +562,14 @@ trait FilesAbstraction 'h' => $file->height ?? $attributes['h'], ], ], + Audio::class => [ + [ + '_' => 'documentAttributeAudio', + 'duration' => $file->duration ?? $attributes['duration'], + 'title' => $file->title ?? $attributes['title'], + 'performer' => $file->performer ?? $attributes['performer'], + ], + ], default => [], }; $attributes[] = ['_' => 'documentAttributeFilename', 'file_name' => $fileName]; @@ -651,6 +745,24 @@ trait FilesAbstraction unset($stream); unset($temp); $file = new ReadableBuffer($file); + } elseif ($type === Audio::class) { + if (Process::start('ffmpeg -version')->join() !== 0) { + $this->logger->logger('Install ffmpeg for audio info extraction!'); + } elseif ($attributes[0]['duration'] === null) { + $file = $this->getStream($file, $cancellation); + $ffmpeg = 'ffmpeg -i pipe: 2>&1'; + $process = Process::start($ffmpeg); + async(fn () => pipe($file, $process->getStdin())); + $output = buffer($process->getStdout()); + if (preg_match('~Duration: (\d{2}:\d{2}:\d{2}\.\d{2})~', $output, $matches)) { + $time = explode(':', $matches[1]); + $hours = (int) $time[0]; + $minutes = (int) $time[1]; + $seconds = (int) $time[2]; + $duration = $hours * 3600 + $minutes * 60 + $seconds; + $attributes[0]['duration'] = $duration; + } + } } $method = 'messages.sendMedia'; @@ -678,6 +790,13 @@ trait FilesAbstraction 'mime_type' => $mimeType, 'attributes' => $attributes, ], + Audio::class => [ + '_' => 'inputMediaUploadedDocument', + 'file' => $file, + 'thumb' => $thumb, + 'mime_type' => $mimeType, + 'attributes' => $attributes, + ], default => [ '_' => 'inputMediaUploadedDocument', 'spoiler' => $spoiler, @@ -692,7 +811,7 @@ trait FilesAbstraction if ($reuseId) { $media['_'] = match ($type) { Photo::class => 'inputMediaPhoto', - Sticker::class, Document::class, Video::class => 'inputMediaDocument', + default => 'inputMediaDocument', }; $media['id'] = $reuseId; } else { From 970b2dd6756d341019ce2fb4b4b305b3cac34958 Mon Sep 17 00:00:00 2001 From: amir Date: Tue, 26 Mar 2024 16:54:39 +0330 Subject: [PATCH 09/24] Add sendVoice method --- src/InternalDoc.php | 31 +++++++++ src/MTProtoTools/FilesAbstraction.php | 98 ++++++++++++++++++++++++++- 2 files changed, 127 insertions(+), 2 deletions(-) diff --git a/src/InternalDoc.php b/src/InternalDoc.php index 6687c3177..3541c1461 100644 --- a/src/InternalDoc.php +++ b/src/InternalDoc.php @@ -1832,6 +1832,37 @@ abstract class InternalDoc { return $this->wrapper->getAPI()->sendAudio($peer, $file, $thumb, $caption, $parseMode, $callback, $fileName, $duration, $title, $performer, $replyToMsgId, $topMsgId, $replyMarkup, $sendAs, $scheduleDate, $silent, $noForwards, $background, $clearDraft, $forceResend, $cancellation); } + /** + * Sends a voice. + * + * Please use named arguments to call this method. + * + * @param integer|string $peer Destination peer or username. + * @param Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream $file File to upload: can be a message to reuse media present in a message. + * @param string $caption Caption of document + * @param ParseMode $parseMode Text parse mode for the caption + * @param ?callable(float, float, int) $callback Upload callback (percent, speed in mpbs, time elapsed) + * @param ?string $fileName Optional file name, if absent will be extracted from the passed $file. + * @param integer|null $ttl Time to live + * @param integer|null $duration Duration of the voice + * @param array|null $waveform Waveform of the voice + * @param integer|null $replyToMsgId ID of message to reply to. + * @param integer|null $topMsgId ID of thread where to send the message. + * @param array|null $replyMarkup Keyboard information. + * @param integer|string|null $sendAs Peer to send the message as. + * @param integer|null $scheduleDate Schedule date. + * @param boolean $silent Whether to send the message silently, without triggering notifications. + * @param boolean $noForwards Whether to disable forwards for this message. + * @param boolean $background Send this message as background message + * @param boolean $clearDraft Clears the draft field + * @param boolean $forceResend Whether to forcefully resend the file, even if its type and name are the same. + * @param ?Cancellation $cancellation Cancellation. + * + */ + final public function sendVoice(string|int $peer, \danog\MadelineProto\EventHandler\Message|\danog\MadelineProto\EventHandler\Media|\danog\MadelineProto\LocalFile|\danog\MadelineProto\RemoteUrl|\danog\MadelineProto\BotApiFileId|\Amp\ByteStream\ReadableStream $file, string $caption = '', \danog\MadelineProto\ParseMode $parseMode = \danog\MadelineProto\ParseMode::TEXT, ?callable $callback = null, ?string $fileName = null, ?int $ttl = null, ?int $duration = null, ?array $waveform = null, ?int $replyToMsgId = null, ?int $topMsgId = null, ?array $replyMarkup = null, string|int|null $sendAs = null, ?int $scheduleDate = null, bool $silent = false, bool $noForwards = false, bool $background = false, bool $clearDraft = false, bool $forceResend = false, ?\Amp\Cancellation $cancellation = null): \danog\MadelineProto\EventHandler\Message + { + return $this->wrapper->getAPI()->sendVoice($peer, $file, $caption, $parseMode, $callback, $fileName, $ttl, $duration, $waveform, $replyToMsgId, $topMsgId, $replyMarkup, $sendAs, $scheduleDate, $silent, $noForwards, $background, $clearDraft, $forceResend, $cancellation); + } /** * Set NOOP update handler, ignoring all updates. */ diff --git a/src/MTProtoTools/FilesAbstraction.php b/src/MTProtoTools/FilesAbstraction.php index 59d12bc28..f0214ceeb 100644 --- a/src/MTProtoTools/FilesAbstraction.php +++ b/src/MTProtoTools/FilesAbstraction.php @@ -32,6 +32,7 @@ use danog\MadelineProto\EventHandler\Media\Document; use danog\MadelineProto\EventHandler\Media\Photo; use danog\MadelineProto\EventHandler\Media\Sticker; use danog\MadelineProto\EventHandler\Media\Video; +use danog\MadelineProto\EventHandler\Media\Voice; use danog\MadelineProto\EventHandler\Message; use danog\MadelineProto\Exception; use danog\MadelineProto\LocalFile; @@ -43,7 +44,6 @@ use Webmozart\Assert\Assert; use function Amp\async; use function Amp\ByteStream\buffer; -use function Amp\ByteStream\getStderr; use function Amp\ByteStream\pipe; /** @@ -481,7 +481,87 @@ trait FilesAbstraction cancellation: $cancellation ); } + /** + * Sends a voice. + * + * Please use named arguments to call this method. + * + * @param integer|string $peer Destination peer or username. + * @param Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream $file File to upload: can be a message to reuse media present in a message. + * @param string $caption Caption of document + * @param ParseMode $parseMode Text parse mode for the caption + * @param ?callable(float, float, int) $callback Upload callback (percent, speed in mpbs, time elapsed) + * @param ?string $fileName Optional file name, if absent will be extracted from the passed $file. + * @param integer|null $ttl Time to live + * @param integer|null $duration Duration of the voice + * @param array|null $waveform Waveform of the voice + * @param integer|null $replyToMsgId ID of message to reply to. + * @param integer|null $topMsgId ID of thread where to send the message. + * @param array|null $replyMarkup Keyboard information. + * @param integer|string|null $sendAs Peer to send the message as. + * @param integer|null $scheduleDate Schedule date. + * @param boolean $silent Whether to send the message silently, without triggering notifications. + * @param boolean $noForwards Whether to disable forwards for this message. + * @param boolean $background Send this message as background message + * @param boolean $clearDraft Clears the draft field + * @param boolean $forceResend Whether to forcefully resend the file, even if its type and name are the same. + * @param ?Cancellation $cancellation Cancellation. + * + */ + public function sendVoice( + int|string $peer, + Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream $file, + string $caption = '', + ParseMode $parseMode = ParseMode::TEXT, + ?callable $callback = null, + ?string $fileName = null, + ?int $ttl = null, + ?int $duration = null, + ?array $waveform = null, + ?int $replyToMsgId = null, + ?int $topMsgId = null, + ?array $replyMarkup = null, + int|string|null $sendAs = null, + ?int $scheduleDate = null, + bool $silent = false, + bool $noForwards = false, + bool $background = false, + bool $clearDraft = false, + bool $forceResend = false, + ?Cancellation $cancellation = null, + ): Message { + $attributes = [ + 'duration' => $duration, + 'waveform' => $waveform, + ]; + return $this->sendMedia( + type: Voice::class, + mimeType: 'audio/ogg', + thumb: null, + attributes: $attributes, + peer: $peer, + file: $file, + caption: $caption, + parseMode: $parseMode, + callback: $callback, + fileName: $fileName, + ttl: $ttl, + spoiler: null, + silent: $silent, + background: $background, + clearDraft: $clearDraft, + noForwards: $noForwards, + updateStickersetsOrder: false, + replyToMsgId: $replyToMsgId, + topMsgId: $topMsgId, + replyMarkup: $replyMarkup, + scheduleDate: $scheduleDate, + sendAs: $sendAs, + forceResend: $forceResend, + cancellation: $cancellation + ); + } /** * Sends a media. * @@ -570,6 +650,14 @@ trait FilesAbstraction 'performer' => $file->performer ?? $attributes['performer'], ], ], + Voice::class => [ + [ + '_' => 'documentAttributeAudio', + 'voice' => true, + 'duration' => $file->duration ?? $attributes['duration'], + 'waveform' => $file->waveform ?? $attributes['waveform'], + ], + ], default => [], }; $attributes[] = ['_' => 'documentAttributeFilename', 'file_name' => $fileName]; @@ -745,7 +833,7 @@ trait FilesAbstraction unset($stream); unset($temp); $file = new ReadableBuffer($file); - } elseif ($type === Audio::class) { + } elseif ($type === Audio::class or $type === Voice::class) { if (Process::start('ffmpeg -version')->join() !== 0) { $this->logger->logger('Install ffmpeg for audio info extraction!'); } elseif ($attributes[0]['duration'] === null) { @@ -797,6 +885,12 @@ trait FilesAbstraction 'mime_type' => $mimeType, 'attributes' => $attributes, ], + Voice::class => [ + '_' => 'inputMediaUploadedDocument', + 'file' => $file, + 'mime_type' => $mimeType, + 'attributes' => $attributes, + ], default => [ '_' => 'inputMediaUploadedDocument', 'spoiler' => $spoiler, From e7ea239256ed2c01c3e81034b86df193943f9670 Mon Sep 17 00:00:00 2001 From: amir Date: Tue, 26 Mar 2024 17:17:17 +0330 Subject: [PATCH 10/24] Add sendGif method --- src/InternalDoc.php | 31 ++++++++++ src/MTProtoTools/FilesAbstraction.php | 87 +++++++++++++++++++++++++++ 2 files changed, 118 insertions(+) diff --git a/src/InternalDoc.php b/src/InternalDoc.php index 3541c1461..85ca5cd59 100644 --- a/src/InternalDoc.php +++ b/src/InternalDoc.php @@ -1800,6 +1800,37 @@ abstract class InternalDoc { return $this->wrapper->getAPI()->sendVideo($peer, $file, $thumb, $caption, $parseMode, $callback, $fileName, $ttl, $spoiler, $roundMessage, $supportsStreaming, $noSound, $duration, $width, $height, $replyToMsgId, $topMsgId, $replyMarkup, $sendAs, $scheduleDate, $silent, $noForwards, $background, $clearDraft, $forceResend, $cancellation); } + /** + * Sends a gif. + * + * Please use named arguments to call this method. + * + * @param integer|string $peer Destination peer or username. + * @param Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream $file File to upload: can be a message to reuse media present in a message. + * @param Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream|null $thumb Optional: Thumbnail to upload + * @param string $caption Caption of document + * @param ParseMode $parseMode Text parse mode for the caption + * @param ?callable(float, float, int) $callback Upload callback (percent, speed in mpbs, time elapsed) + * @param ?string $fileName Optional file name, if absent will be extracted from the passed $file. + * @param integer|null $ttl Time to live + * @param boolean $spoiler Whether the message is a spoiler + * @param integer|null $replyToMsgId ID of message to reply to. + * @param integer|null $topMsgId ID of thread where to send the message. + * @param array|null $replyMarkup Keyboard information. + * @param integer|string|null $sendAs Peer to send the message as. + * @param integer|null $scheduleDate Schedule date. + * @param boolean $silent Whether to send the message silently, without triggering notifications. + * @param boolean $noForwards Whether to disable forwards for this message. + * @param boolean $background Send this message as background message + * @param boolean $clearDraft Clears the draft field + * @param boolean $forceResend Whether to forcefully resend the file, even if its type and name are the same. + * @param ?Cancellation $cancellation Cancellation. + * + */ + final public function sendGif(string|int $peer, \danog\MadelineProto\EventHandler\Message|\danog\MadelineProto\EventHandler\Media|\danog\MadelineProto\LocalFile|\danog\MadelineProto\RemoteUrl|\danog\MadelineProto\BotApiFileId|\Amp\ByteStream\ReadableStream $file, \danog\MadelineProto\EventHandler\Message|\danog\MadelineProto\EventHandler\Media|\danog\MadelineProto\LocalFile|\danog\MadelineProto\RemoteUrl|\danog\MadelineProto\BotApiFileId|\Amp\ByteStream\ReadableStream|null $thumb = null, string $caption = '', \danog\MadelineProto\ParseMode $parseMode = \danog\MadelineProto\ParseMode::TEXT, ?callable $callback = null, ?string $fileName = null, ?int $ttl = null, bool $spoiler = false, ?int $replyToMsgId = null, ?int $topMsgId = null, ?array $replyMarkup = null, string|int|null $sendAs = null, ?int $scheduleDate = null, bool $silent = false, bool $noForwards = false, bool $background = false, bool $clearDraft = false, bool $forceResend = false, ?\Amp\Cancellation $cancellation = null): \danog\MadelineProto\EventHandler\Message + { + return $this->wrapper->getAPI()->sendGif($peer, $file, $thumb, $caption, $parseMode, $callback, $fileName, $ttl, $spoiler, $replyToMsgId, $topMsgId, $replyMarkup, $sendAs, $scheduleDate, $silent, $noForwards, $background, $clearDraft, $forceResend, $cancellation); + } /** * Sends an audio. * diff --git a/src/MTProtoTools/FilesAbstraction.php b/src/MTProtoTools/FilesAbstraction.php index f0214ceeb..615fe7c4a 100644 --- a/src/MTProtoTools/FilesAbstraction.php +++ b/src/MTProtoTools/FilesAbstraction.php @@ -29,6 +29,7 @@ use danog\MadelineProto\BotApiFileId; use danog\MadelineProto\EventHandler\Media; use danog\MadelineProto\EventHandler\Media\Audio; use danog\MadelineProto\EventHandler\Media\Document; +use danog\MadelineProto\EventHandler\Media\Gif; use danog\MadelineProto\EventHandler\Media\Photo; use danog\MadelineProto\EventHandler\Media\Sticker; use danog\MadelineProto\EventHandler\Media\Video; @@ -397,6 +398,82 @@ trait FilesAbstraction cancellation: $cancellation ); } + /** + * Sends a gif. + * + * Please use named arguments to call this method. + * + * @param integer|string $peer Destination peer or username. + * @param Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream $file File to upload: can be a message to reuse media present in a message. + * @param Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream|null $thumb Optional: Thumbnail to upload + * @param string $caption Caption of document + * @param ParseMode $parseMode Text parse mode for the caption + * @param ?callable(float, float, int) $callback Upload callback (percent, speed in mpbs, time elapsed) + * @param ?string $fileName Optional file name, if absent will be extracted from the passed $file. + * @param integer|null $ttl Time to live + * @param boolean $spoiler Whether the message is a spoiler + * @param integer|null $replyToMsgId ID of message to reply to. + * @param integer|null $topMsgId ID of thread where to send the message. + * @param array|null $replyMarkup Keyboard information. + * @param integer|string|null $sendAs Peer to send the message as. + * @param integer|null $scheduleDate Schedule date. + * @param boolean $silent Whether to send the message silently, without triggering notifications. + * @param boolean $noForwards Whether to disable forwards for this message. + * @param boolean $background Send this message as background message + * @param boolean $clearDraft Clears the draft field + * @param boolean $forceResend Whether to forcefully resend the file, even if its type and name are the same. + * @param ?Cancellation $cancellation Cancellation. + * + */ + public function sendGif( + int|string $peer, + Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream $file, + Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream|null $thumb = null, + string $caption = '', + ParseMode $parseMode = ParseMode::TEXT, + ?callable $callback = null, + ?string $fileName = null, + ?int $ttl = null, + bool $spoiler = false, + ?int $replyToMsgId = null, + ?int $topMsgId = null, + ?array $replyMarkup = null, + int|string|null $sendAs = null, + ?int $scheduleDate = null, + bool $silent = false, + bool $noForwards = false, + bool $background = false, + bool $clearDraft = false, + bool $forceResend = false, + ?Cancellation $cancellation = null, + ): Message { + return $this->sendMedia( + type: Gif::class, + mimeType: 'image/gif', + thumb: $thumb, + attributes: [], + peer: $peer, + file: $file, + caption: $caption, + parseMode: $parseMode, + callback: $callback, + fileName: $fileName, + ttl: $ttl, + spoiler: $spoiler, + silent: $silent, + background: $background, + clearDraft: $clearDraft, + noForwards: $noForwards, + updateStickersetsOrder: false, + replyToMsgId: $replyToMsgId, + topMsgId: $topMsgId, + replyMarkup: $replyMarkup, + scheduleDate: $scheduleDate, + sendAs: $sendAs, + forceResend: $forceResend, + cancellation: $cancellation + ); + } /** * Sends an audio. * @@ -658,6 +735,7 @@ trait FilesAbstraction 'waveform' => $file->waveform ?? $attributes['waveform'], ], ], + Gif::class => [['_' => 'documentAttributeAnimated']], default => [], }; $attributes[] = ['_' => 'documentAttributeFilename', 'file_name' => $fileName]; @@ -878,6 +956,15 @@ trait FilesAbstraction 'mime_type' => $mimeType, 'attributes' => $attributes, ], + Gif::class => [ + '_' => 'inputMediaUploadedDocument', + 'spoiler' => $spoiler, + 'ttl_seconds' => $ttl, + 'file' => $file, + 'thumb' => $thumb, + 'mime_type' => $mimeType, + 'attributes' => $attributes, + ], Audio::class => [ '_' => 'inputMediaUploadedDocument', 'file' => $file, From 561b4be55a81e2671784367ad226898e7531770f Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Sun, 7 Apr 2024 18:24:43 +0200 Subject: [PATCH 11/24] Small bugfix --- src/Serialization.php | 11 +++++++---- tools/phar.php | 2 +- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/Serialization.php b/src/Serialization.php index 2de4d62b7..8a434e88f 100644 --- a/src/Serialization.php +++ b/src/Serialization.php @@ -223,15 +223,18 @@ abstract class Serialization if ($settings instanceof Settings) { $settings = $settings->getDb(); } + $prefix = null; if ($unserialized instanceof DriverArray || $unserialized instanceof DbArrayBuilder - || (!$exists && $settings instanceof DriverDatabaseAbstract) + || ( + !$exists + && $settings instanceof DriverDatabaseAbstract + && $prefix = $settings->getEphemeralFilesystemPrefix() + ) ) { $tableName = null; $array = null; - if ($settings instanceof DriverDatabaseAbstract - && $prefix = $settings->getEphemeralFilesystemPrefix() - ) { + if ($prefix !== null) { $tableName = "{$prefix}_MTProto_session"; } elseif ($unserialized instanceof DriverArray) { $unserialized = (array) $unserialized; diff --git a/tools/phar.php b/tools/phar.php index 20071230b..b0cacc29a 100644 --- a/tools/phar.php +++ b/tools/phar.php @@ -140,7 +140,7 @@ class Installer 'Content-Type: application/json', sprintf( 'User-Agent: Composer/%s (%s; %s; %s; %s%s)', - 'MProto v7', + 'MP v8', \function_exists('php_uname') ? @php_uname('s') : 'Unknown', \function_exists('php_uname') ? @php_uname('r') : 'Unknown', $phpVersion, From f75db20fd74cc308fdde0fa54e671eb5fb2f053e Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Sun, 7 Apr 2024 18:46:33 +0200 Subject: [PATCH 12/24] Bump --- src/API.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/API.php b/src/API.php index c898b79ad..2c5a69fe5 100644 --- a/src/API.php +++ b/src/API.php @@ -52,7 +52,7 @@ final class API extends AbstractAPI * * @var string */ - public const RELEASE = '8.0.0-beta195'; + public const RELEASE = '8.0.0-beta196'; /** * We're not logged in. * From 894984be95678dca0105d2ef4f5c07b43a1ca506 Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Sun, 7 Apr 2024 19:44:30 +0200 Subject: [PATCH 13/24] Fix PHP 8.2 support --- src/API.php | 2 +- src/polyfill.php | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/API.php b/src/API.php index 2c5a69fe5..b4fbbc34b 100644 --- a/src/API.php +++ b/src/API.php @@ -52,7 +52,7 @@ final class API extends AbstractAPI * * @var string */ - public const RELEASE = '8.0.0-beta196'; + public const RELEASE = '8.0.0-beta197'; /** * We're not logged in. * diff --git a/src/polyfill.php b/src/polyfill.php index b51c67b5b..89e432ad2 100644 --- a/src/polyfill.php +++ b/src/polyfill.php @@ -25,7 +25,6 @@ class_alias(PostgresArray::class, '\\danog\\MadelineProto\\Db\\PostgresArray'); class_alias(PostgresArray::class, '\\danog\\MadelineProto\\Db\\PostgresArrayBytea'); class_alias(RedisArray::class, '\\danog\\MadelineProto\\Db\\RedisArray'); class_alias(CacheContainer::class, '\\danog\\MadelineProto\\Db\\CacheContainer'); -class_alias(Countable::class, '\\danog\\MadelineProto\\Db\\DbArray'); /** @deprecated */ final class MemoryArray extends ArrayObject From 80401c3ea0673d393aa5a6fa0eb263eb6ec25437 Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Sun, 7 Apr 2024 19:52:20 +0200 Subject: [PATCH 14/24] cs-fix --- src/polyfill.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/polyfill.php b/src/polyfill.php index 89e432ad2..57c183a4f 100644 --- a/src/polyfill.php +++ b/src/polyfill.php @@ -8,7 +8,6 @@ if (class_exists('\\danog\\MadelineProto\\Db\\NullCache\\MysqlArray')) { use ArrayObject; use AssertionError; -use Countable; use danog\AsyncOrm\DbArray; use danog\AsyncOrm\DbArrayBuilder; use danog\AsyncOrm\Internal\Containers\CacheContainer; From 1e0c6e1e2010a6d8057d58eebde1c39f3d9ef212 Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Mon, 8 Apr 2024 15:34:04 +0200 Subject: [PATCH 15/24] Fix issue with ORM --- composer.json | 42 +++++++++++++++---------------- docs | 2 +- src/API.php | 2 +- src/MTProto.php | 23 ++++++++++------- src/MTProtoTools/PeerDatabase.php | 2 +- src/Wrappers/Login.php | 2 ++ 6 files changed, 40 insertions(+), 33 deletions(-) diff --git a/composer.json b/composer.json index c36c241b3..bad67c512 100644 --- a/composer.json +++ b/composer.json @@ -24,7 +24,7 @@ }, "require": { "php-64bit": ">=8.2", - "danog/primemodule": "^1", + "danog/primemodule": "^1.0.13", "symfony/polyfill-mbstring": "*", "ext-mbstring": "*", "ext-json": "*", @@ -35,44 +35,44 @@ "ext-zlib": "*", "ext-fileinfo": "*", "amphp/amp": "^3", - "amphp/http-client": "^5", + "amphp/http-client": "^5.0.1", "amphp/websocket-client": "^2", - "amphp/http": "^2", - "amphp/socket": "^2", - "amphp/dns": "^2", - "amphp/byte-stream": "^2", - "amphp/file": "^3.0.1", + "amphp/http": "^2.1.1", + "amphp/socket": "^2.3", + "amphp/dns": "^2.1.1", + "amphp/byte-stream": "^2.1.1", + "amphp/file": "^3.0.2", "amphp/mysql": "^3", "amphp/postgres": "^2", "danog/dns-over-https": "^1", "amphp/http-client-cookies": "^2", "danog/tg-file-decoder": "^1", - "league/uri": "^7", + "league/uri": "^7.4.1", "danog/ipc": "^1", "amphp/log": "^2", - "danog/loop": "^1.1.0", - "phpseclib/phpseclib": "^3.0.22", + "danog/loop": "^1.1.1", + "phpseclib/phpseclib": "^3.0.37", "amphp/redis": "^2", - "psr/http-factory": "^1.0", + "psr/http-factory": "^1.0.2", "psr/log": "^3", "webmozart/assert": "^1.11", - "bacon/bacon-qr-code": "^2.0", - "nikic/php-parser": "^5", - "revolt/event-loop": "^1.0.5", - "danog/async-orm": "^1.0.1", + "bacon/bacon-qr-code": "^2.0.8", + "nikic/php-parser": "^5.0.2", + "revolt/event-loop": "^1.0.6", + "danog/async-orm": "^1.0.2", "symfony/thanks": "^1.3" }, "require-dev": { "ext-ctype": "*", - "danog/phpdoc": "^0.1.7", - "phpunit/phpunit": "^9", + "danog/phpdoc": "^0.1.24", + "phpunit/phpunit": "^9.6.19", "amphp/phpunit-util": "^3", "bamarni/composer-bin-plugin": "1.8.2", - "symfony/yaml": "^6.0", - "amphp/http-server": "^3", - "revolt/event-loop-adapter-react": "^1", + "symfony/yaml": "^6.4.3", + "amphp/http-server": "^3.3", + "revolt/event-loop-adapter-react": "^1.1.1", "dg/bypass-finals": "dev-master", - "brianium/paratest": "^6.11" + "brianium/paratest": "^6.11.1" }, "suggest": { "ext-primemodule": "Install the primemodule and FFI extensions to speed up MadelineProto (https://prime.madelineproto.xyz)", diff --git a/docs b/docs index 821000468..11626857a 160000 --- a/docs +++ b/docs @@ -1 +1 @@ -Subproject commit 821000468d14f943496b8bbc7183360c83630d6b +Subproject commit 11626857a0bfe4d094f4f43ad63b6df6364b6547 diff --git a/src/API.php b/src/API.php index b4fbbc34b..1efeb0eb5 100644 --- a/src/API.php +++ b/src/API.php @@ -52,7 +52,7 @@ final class API extends AbstractAPI * * @var string */ - public const RELEASE = '8.0.0-beta197'; + public const RELEASE = '8.0.0-beta198'; /** * We're not logged in. * diff --git a/src/MTProto.php b/src/MTProto.php index 522f69ad2..1eb69d3f1 100644 --- a/src/MTProto.php +++ b/src/MTProto.php @@ -896,15 +896,7 @@ final class MTProto implements TLCallback, LoggerGetter, SettingsGetter $this->minDatabase ??= new MinDatabase($this); $this->peerDatabase ??= new PeerDatabase($this); - $db = []; - $db []= async($this->referenceDatabase->init(...)); - $db []= async($this->minDatabase->init(...)); - $db []= async($this->peerDatabase->init(...)); - $db []= async($this->internalInitDbProperties(...), $this->getDbSettings(), $this->getDbPrefix().'_MTProto_'); - foreach ($this->secretChats as $chat) { - $db []= async($chat->init(...)); - } - await($db); + $this->initDb(); if (!isset($this->TL)) { $this->TL = new TL($this); @@ -924,6 +916,19 @@ final class MTProto implements TLCallback, LoggerGetter, SettingsGetter $this->seqUpdater ??= new SeqLoop($this); } + /** @internal */ + public function initDb(): void + { + $db = []; + $db []= async($this->referenceDatabase->init(...)); + $db []= async($this->minDatabase->init(...)); + $db []= async($this->peerDatabase->init(...)); + $db []= async($this->internalInitDbProperties(...), $this->getDbSettings(), $this->getDbPrefix().'_MTProto_'); + foreach ($this->secretChats as $chat) { + $db []= async($chat->init(...)); + } + await($db); + } /** * Upgrade MadelineProto instance. */ diff --git a/src/MTProtoTools/PeerDatabase.php b/src/MTProtoTools/PeerDatabase.php index ed7ee2c59..e976ef4be 100644 --- a/src/MTProtoTools/PeerDatabase.php +++ b/src/MTProtoTools/PeerDatabase.php @@ -497,7 +497,7 @@ final class PeerDatabase implements TLCallback ) { $existingChat = $this->db[$chat['id']]; if (!$existingChat || $existingChat != $chat) { - $this->API->logger("Updated chat -{$chat['id']}", Logger::ULTRA_VERBOSE); + $this->API->logger("Updated chat {$chat['id']}", Logger::ULTRA_VERBOSE); if (!$this->API->settings->getDb()->getEnablePeerInfoDb()) { $chat = [ '_' => $chat['_'], diff --git a/src/Wrappers/Login.php b/src/Wrappers/Login.php index 9008fd1db..b0c77c99d 100644 --- a/src/Wrappers/Login.php +++ b/src/Wrappers/Login.php @@ -355,6 +355,8 @@ trait Login $this->fullGetSelf(); $this->getPhoneConfig(); $this->startUpdateSystem(); + $this->initDb(); + $this->serialize(); return $authorization; } /** From 0c25881c272239d88963335629b4d8b141688314 Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Mon, 8 Apr 2024 21:07:40 +0200 Subject: [PATCH 16/24] IPC improvement --- src/Ipc/Client.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Ipc/Client.php b/src/Ipc/Client.php index c8d0a226f..a9836f66a 100644 --- a/src/Ipc/Client.php +++ b/src/Ipc/Client.php @@ -241,6 +241,7 @@ final class Client extends ClientAbstract $method === 'messages.editInlineBotMessage' || $method === 'messages.uploadMedia' || $method === 'messages.sendMedia' || + $method === 'stories.sendStory' || $method === 'messages.editMessage' ) && isset($args['media']) && \is_array($args['media'])) { $this->processMedia($args['media'], $args['cancellation'] ?? null, true); From 2d03f871eec8fb13fa3733369709f0f9c9880349 Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Thu, 11 Apr 2024 20:19:19 +0200 Subject: [PATCH 17/24] Fixe peer database --- src/MTProtoTools/PeerDatabase.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/MTProtoTools/PeerDatabase.php b/src/MTProtoTools/PeerDatabase.php index e976ef4be..6b68e9759 100644 --- a/src/MTProtoTools/PeerDatabase.php +++ b/src/MTProtoTools/PeerDatabase.php @@ -57,17 +57,17 @@ final class PeerDatabase implements TLCallback * * @var DbArray */ - #[OrmMappedArray(KeyType::INT, ValueType::SCALAR, tablePostfix: 'MTProto_chats')] + #[OrmMappedArray(KeyType::INT, ValueType::SCALAR, tablePostfix: '_MTProto_chats')] private $db; /** * @var DbArray */ - #[OrmMappedArray(KeyType::INT, ValueType::SCALAR, tablePostfix: 'MTProto_full_chats')] + #[OrmMappedArray(KeyType::INT, ValueType::SCALAR, tablePostfix: '_MTProto_full_chats')] private $fullDb; /** * @var DbArray */ - #[OrmMappedArray(KeyType::STRING, ValueType::INT)] + #[OrmMappedArray(KeyType::STRING, ValueType::INT, tablePostfix: '_PeerDatabase_usernames')] private $usernames; private bool $hasInfo = true; private bool $hasUsernames = true; @@ -95,7 +95,7 @@ final class PeerDatabase implements TLCallback } public function init(): void { - $this->initDbProperties($this->API->getDbSettings(), $this->API->getDbPrefix().'_PeerDatabase_'); + $this->initDbProperties($this->API->getDbSettings(), $this->API->getDbPrefix()); if (!$this->API->settings->getDb()->getEnableFullPeerDb()) { $this->fullDb->clear(); } From 0dce27db79af17998a52937e679f600b85c6d13a Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Thu, 11 Apr 2024 20:30:08 +0200 Subject: [PATCH 18/24] Move legacy classes --- src/Db/CachedArray.php | 48 +++++++++++++++++++++++++++ src/Db/MemoryArray.php | 45 +++++++++++++++++++++++++ src/polyfill.php | 75 ------------------------------------------ 3 files changed, 93 insertions(+), 75 deletions(-) create mode 100644 src/Db/CachedArray.php create mode 100644 src/Db/MemoryArray.php diff --git a/src/Db/CachedArray.php b/src/Db/CachedArray.php new file mode 100644 index 000000000..e20930a07 --- /dev/null +++ b/src/Db/CachedArray.php @@ -0,0 +1,48 @@ + Date: Thu, 11 Apr 2024 21:03:36 +0200 Subject: [PATCH 19/24] Bump --- langs/fa.json | 3 ++- src/Lang.php | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/langs/fa.json b/langs/fa.json index 4cee0e0ec..ae67208d3 100644 --- a/langs/fa.json +++ b/langs/fa.json @@ -157,5 +157,6 @@ "do_not_use_yield": "مدلین‌پروتو نسخه 8 نیازی به yield در توابع async ندارد و حتی آن را پشتیبانی هم نمیکند، شما باید تمام کلمات کلیدی yield را که قبلا در اجرای توابع async به کار میرفتند حذف کنید", "windows_warning": "برای کاربران ویندوزی: لطفا اگر این ناموفق بود به لینوکس کوچ کنید. همچنین میتوانید تلاش کنید تا تنظیمات فایروال خود را تغییر دهید تا به تمام پروسه‌های PHP اجازه ساخت سوکت را بدهید (100 درصد تغییر سیستم به لینوکس آسان‌تر است، مدلین‌پروتو در لینوکس خارج از باکس کار میکند، نیازی به تغییر ندارد)", "translate_madelineproto_web": "مدلین‌پروتو را می‌توان به زبان شما ترجمه کرد (پیشرفت ترجمه فعلی: %d%%)، برای مشارکت در ترجمه اینجا را کلیک کنید!", - "translate_madelineproto_cli": "مدلین‌پروتو را می‌توان به زبان شما ترجمه کرد (پیشرفت ترجمه فعلی: %d%%)، برای مشارکت در ترجمه به https://weblate.madelineproto.xyz بروید!" + "translate_madelineproto_cli": "مدلین‌پروتو را می‌توان به زبان شما ترجمه کرد (پیشرفت ترجمه فعلی: %d%%)، برای مشارکت در ترجمه به https://weblate.madelineproto.xyz بروید!", + "baseDirLimitation": "یک محدودیت basedir پیکربندی شده است: این میتواند روی پرفورمنس تاثیر بگذارد و باعث ایجاد مشکلات شود، لطفا در صورت امکان آن را غیرفعال کنید!" } diff --git a/src/Lang.php b/src/Lang.php index e84241ecd..088e062c1 100644 --- a/src/Lang.php +++ b/src/Lang.php @@ -27,7 +27,7 @@ final class Lang 'he' => 74, 'it' => 99, 'ckb' => 74, - 'fa' => 99, + 'fa' => 100, 'ru' => 53, 'uz' => 75, ]; @@ -578,7 +578,7 @@ If you intentionally deleted this account, ignore this message.', 'apiParamsError' => 'شما تمام ورودی‌های موردنیاز را وارد نکردید!', 'api_not_set' => 'شما باید یک api key و یک api id وارد کنید، @ خوتان را از my.telegram.org بگیرید', 'array_invalid' => 'شما یک آرایه معتبر وارد نکردید', - 'baseDirLimitation' => 'A basedir limitation is configured: this can impact performance and cause some issues, please disable it if possible!', + 'baseDirLimitation' => 'یک محدودیت basedir پیکربندی شده است: این میتواند روی پرفورمنس تاثیر بگذارد و باعث ایجاد مشکلات شود، لطفا در صورت امکان آن را غیرفعال کنید!', 'bool_error' => 'نمی‌توان‌ boolean را استخراج کرد', 'botAlreadyRunning' => 'ربات هم اکنون در حال اجرا است!', 'botapi_conversion_error' => 'نمیتوان %s را به شی ربات API تبدیل کرد', From 8485b469346722861dc19684aba9edbfb3ff33b6 Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Thu, 11 Apr 2024 21:47:48 +0200 Subject: [PATCH 20/24] Bump --- src/API.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/API.php b/src/API.php index 1efeb0eb5..24b8596f6 100644 --- a/src/API.php +++ b/src/API.php @@ -52,7 +52,7 @@ final class API extends AbstractAPI * * @var string */ - public const RELEASE = '8.0.0-beta198'; + public const RELEASE = '8.0.0-beta199'; /** * We're not logged in. * From d256e0875ba41ad71b4f9ec6ef67738c7201702a Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Mon, 15 Apr 2024 21:53:01 +0200 Subject: [PATCH 21/24] Cleanup --- composer.json | 11 ++--- docs | 2 +- psalm.xml | 4 +- src/BotApiFileId.php | 7 ++- src/Conversion.php | 2 +- src/DataCenter.php | 2 +- src/DoHWrapper.php | 2 +- src/StrTools.php | 85 ++++------------------------------ vendor-bin/check/composer.json | 2 - 9 files changed, 25 insertions(+), 92 deletions(-) diff --git a/composer.json b/composer.json index bad67c512..80158b853 100644 --- a/composer.json +++ b/composer.json @@ -60,7 +60,8 @@ "nikic/php-parser": "^5.0.2", "revolt/event-loop": "^1.0.6", "danog/async-orm": "^1.0.2", - "symfony/thanks": "^1.3" + "symfony/thanks": "^1.3", + "danog/telegram-entities": "^1.0" }, "require-dev": { "ext-ctype": "*", @@ -72,7 +73,8 @@ "amphp/http-server": "^3.3", "revolt/event-loop-adapter-react": "^1.1.1", "dg/bypass-finals": "dev-master", - "brianium/paratest": "^6.11.1" + "brianium/paratest": "^6.11.1", + "vimeo/psalm": "dev-master" }, "suggest": { "ext-primemodule": "Install the primemodule and FFI extensions to speed up MadelineProto (https://prime.madelineproto.xyz)", @@ -132,11 +134,6 @@ "post-install-cmd": ["@composer bin all install --ansi"], "post-update-cmd": ["@composer bin all update --ansi"] }, - "extra": { - "phabel": { - "revision": 0 - } - }, "config": { "allow-plugins": { "bamarni/composer-bin-plugin": true, diff --git a/docs b/docs index 11626857a..87f2e3f97 160000 --- a/docs +++ b/docs @@ -1 +1 @@ -Subproject commit 11626857a0bfe4d094f4f43ad63b6df6364b6547 +Subproject commit 87f2e3f97142013572b6f5615fc8a336eeebc18e diff --git a/psalm.xml b/psalm.xml index c4f35f530..424bf1803 100644 --- a/psalm.xml +++ b/psalm.xml @@ -8,9 +8,9 @@ ignoreInternalFunctionNullReturn="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="https://getpsalm.org/schema/config" - xsi:schemaLocation="https://getpsalm.org/schema/config vendor-bin/check/vendor/vimeo/psalm/config.xsd" + xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd" errorBaseline="psalm-baseline.xml" - autoloader="vendor-bin/check/vendor/autoload.php" + autoloader="vendor/autoload.php" > diff --git a/src/BotApiFileId.php b/src/BotApiFileId.php index ca3eaa2bc..d7cc4ba96 100644 --- a/src/BotApiFileId.php +++ b/src/BotApiFileId.php @@ -16,6 +16,7 @@ namespace danog\MadelineProto; +use AssertionError; use danog\Decoder\FileId; use danog\Decoder\FileIdType; use danog\MadelineProto\EventHandler\Media; @@ -54,7 +55,8 @@ final class BotApiFileId */ public function getTypeClass(): string { - return match (FileId::fromBotAPI($this->fileId)->type) { + $f = FileId::fromBotAPI($this->fileId); + return match ($f->type) { FileIdType::PHOTO => Photo::class, FileIdType::VOICE => Voice::class, FileIdType::VIDEO => Video::class, @@ -62,7 +64,8 @@ final class BotApiFileId FileIdType::STICKER => AbstractSticker::class, FileIdType::VIDEO_NOTE => RoundVideo::class, FileIdType::AUDIO => Audio::class, - FileIdType::ANIMATION => Gif::class + FileIdType::ANIMATION => Gif::class, + default => throw new AssertionError("Cannot use bot API file ID of type ".$f->type->value) }; } } diff --git a/src/Conversion.php b/src/Conversion.php index dac0aae0b..a46f2a5c7 100644 --- a/src/Conversion.php +++ b/src/Conversion.php @@ -31,7 +31,7 @@ use function stream_get_contents; final class Conversion { /** - * Prepare API instance. + * Import authorization from raw auth key and DC id. * * @param array $authorization Authorization info, DC ID => auth key */ diff --git a/src/DataCenter.php b/src/DataCenter.php index 646551c9d..11ebfd279 100644 --- a/src/DataCenter.php +++ b/src/DataCenter.php @@ -140,7 +140,7 @@ final class DataCenter * * @internal */ - private static function normalizeBindToOption(string $bindTo = null): ?string + private static function normalizeBindToOption(?string $bindTo = null): ?string { if ($bindTo === null) { return null; diff --git a/src/DoHWrapper.php b/src/DoHWrapper.php index d12c589ab..22d309ee0 100644 --- a/src/DoHWrapper.php +++ b/src/DoHWrapper.php @@ -128,7 +128,7 @@ final class DoHWrapper * * @return ConnectionContext[] */ - public function generateContexts(string $uri, ConnectContext $context = null): array + public function generateContexts(string $uri, ?ConnectContext $context = null): array { $ctxs = []; $combos = [ diff --git a/src/StrTools.php b/src/StrTools.php index 41a98dbb1..342196ac1 100644 --- a/src/StrTools.php +++ b/src/StrTools.php @@ -40,8 +40,9 @@ use danog\MadelineProto\EventHandler\Message\Entities\Url; use danog\MadelineProto\TL\Conversion\DOMEntities; use danog\MadelineProto\TL\Conversion\Extension; use danog\MadelineProto\TL\Conversion\MarkdownEntities; +use danog\TelegramEntities\Entities; +use danog\TelegramEntities\EntityTools; use Throwable; -use Webmozart\Assert\Assert; /** * Some tools. @@ -55,15 +56,7 @@ abstract class StrTools extends Extension */ public static function mbStrlen(string $text): int { - $length = 0; - $textlength = \strlen($text); - for ($x = 0; $x < $textlength; $x++) { - $char = \ord($text[$x]); - if (($char & 0xc0) != 0x80) { - $length += 1 + ($char >= 0xf0 ? 1 : 0); - } - } - return $length; + return EntityTools::mbStrlen($text); } /** * Telegram UTF-8 multibyte substring. @@ -74,15 +67,7 @@ abstract class StrTools extends Extension */ public static function mbSubstr(string $text, int $offset, ?int $length = null): string { - return mb_convert_encoding( - substr( - mb_convert_encoding($text, 'UTF-16'), - $offset<<1, - $length === null ? null : ($length<<1), - ), - 'UTF-8', - 'UTF-16', - ); + return EntityTools::mbSubstr($text, $offset, $length); } /** * Telegram UTF-8 multibyte split. @@ -93,13 +78,7 @@ abstract class StrTools extends Extension */ public static function mbStrSplit(string $text, int $length): array { - $result = []; - foreach (str_split(mb_convert_encoding($text, 'UTF-16'), $length<<1) as $chunk) { - $chunk = mb_convert_encoding($chunk, 'UTF-8', 'UTF-16'); - Assert::string($chunk); - $result []= $chunk; - } - return $result; + return EntityTools::mbStrSplit($text, $length); } /** * Manually convert HTML to a message and a set of entities. @@ -222,7 +201,7 @@ abstract class StrTools extends Extension */ public static function htmlEscape(string $what): string { - return htmlspecialchars($what, ENT_QUOTES|ENT_SUBSTITUTE|ENT_XML1); + return EntityTools::htmlEscape($what); } /** * Escape string for markdown. @@ -231,51 +210,7 @@ abstract class StrTools extends Extension */ public static function markdownEscape(string $what): string { - return str_replace( - [ - '\\', - '_', - '*', - '[', - ']', - '(', - ')', - '~', - '`', - '>', - '#', - '+', - '-', - '=', - '|', - '{', - '}', - '.', - '!', - ], - [ - '\\\\', - '\\_', - '\\*', - '\\[', - '\\]', - '\\(', - '\\)', - '\\~', - '\\`', - '\\>', - '\\#', - '\\+', - '\\-', - '\\=', - '\\|', - '\\{', - '\\}', - '\\.', - '\\!', - ], - $what - ); + return EntityTools::markdownEscape($what); } /** * Escape string for markdown codeblock. @@ -284,7 +219,7 @@ abstract class StrTools extends Extension */ public static function markdownCodeblockEscape(string $what): string { - return str_replace('```', '\\```', $what); + return EntityTools::markdownCodeblockEscape($what); } /** * Escape string for markdown code section. @@ -293,7 +228,7 @@ abstract class StrTools extends Extension */ public static function markdownCodeEscape(string $what): string { - return str_replace('`', '\\`', $what); + return EntityTools::markdownCodeEscape($what); } /** * Escape string for URL. @@ -302,7 +237,7 @@ abstract class StrTools extends Extension */ public static function markdownUrlEscape(string $what): string { - return str_replace(')', '\\)', $what); + return EntityTools::markdownUrlEscape($what); } /** * Escape type name. diff --git a/vendor-bin/check/composer.json b/vendor-bin/check/composer.json index fb3a499fd..2a3061de1 100644 --- a/vendor-bin/check/composer.json +++ b/vendor-bin/check/composer.json @@ -1,8 +1,6 @@ { "require": { - "vimeo/psalm": "dev-master", "ennexa/amp-update-cache": "dev-master", - "phpunit/phpunit": "^9", "amphp/php-cs-fixer-config": "v2.x-dev", "slevomat/coding-standard": "^8.7", "squizlabs/php_codesniffer": "^3.7", From 7bec8cf3e85be2108979fa33b11bd3c5f45e773d Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Mon, 15 Apr 2024 22:03:06 +0200 Subject: [PATCH 22/24] Psalm fixes --- .woodpecker/.cs.yml | 14 +- psalm-baseline.xml | 7246 ++++++++++++++++++++++++++---- psalm.xml | 2 +- src/Ipc/ExitFailure.php | 2 +- src/Lang.php | 1 + src/Loop/Connection/ReadLoop.php | 4 +- src/Ogg.php | 1 + tools/translator.php | 1 + 8 files changed, 6390 insertions(+), 881 deletions(-) diff --git a/.woodpecker/.cs.yml b/.woodpecker/.cs.yml index 903273472..12b89937d 100644 --- a/.woodpecker/.cs.yml +++ b/.woodpecker/.cs.yml @@ -27,4 +27,16 @@ steps: - push commands: - apk add bash - - tests/test.sh cs \ No newline at end of file + - tests/test.sh cs + + psalm: + group: test + image: danog/madelineproto:next + when: + event: + - pull_request + - tag + - push + commands: + - apk add bash + - tests/test.sh psalm \ No newline at end of file diff --git a/psalm-baseline.xml b/psalm-baseline.xml index 0ea66f49b..4253a9cdc 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -1,40 +1,57 @@ - + - $settings + __construct($this->session->getSessionDirectoryPath())]]> + + + + + + getFuture()]]> + + + + + + + + + - Logger::$default + - getMessage - wakeup + + - API - API - API - API - API - API - API - API - API - API - API - API - API - API - API - API - API - API - API - API + + + + + + + + + + + + + + + + + + + + + + 10]]> @@ -42,22 +59,25 @@ + + serializeSession($this)]]> + - logger + - $eventHandler + - getMessage - isInited - setEventHandler + + + - !$started - !$started + + 10]]> @@ -66,20 +86,72 @@ - APIStart + + + + + + + + + + + + + + + + + + + + + + + - \is_callable($callable) + + + + + + + + + + + + + + + + API->methodCallAsyncRead( + isset($message['media']) && ( + \is_string($message['media']) + || ( + isset($message['media']['_']) && + $message['media']['_'] !== 'messageMediaWebPage' + ) + ) + ? 'messages.sendMedia' + : 'messages.sendMessage', + array_merge($message, ['peer' => $peer, 'floodWaitLimit' => 2*86400, 'cancellation' => $cancellation]), + )]]> + + + + - $id + @@ -93,6 +165,9 @@ + + + filter->whitelist) && !\in_array($peer, $this->filter->whitelist, true)]]> @@ -105,15 +180,64 @@ getStream()]]> - $API - $datacenter - $shared + + + + + + + + + + + + + + + + getFile()]]> + getFile()]]> + + + + + + + API->getTL()->getConstructors()->findByPredicate($arguments['file']['_'])['type']]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + - getInputClientProxy - isHttp - refreshNextDisable - refreshNextEnable + + + + @@ -121,7 +245,7 @@ - $uri + @@ -129,59 +253,240 @@ + + $key]]]> + - $auth_key - $data - $data - $fileName - $fileName - $fp - $options - $options - $settings + + + + + + + + + - $tdesktop_base_path - $tdesktop_key - $tdesktop_user_base_path + + + - tdesktop - tdesktop_decrypt - tdesktop_fopen - tdesktop_fopen_encrypted - tdesktop_md5 - tdesktop_read_bytearray + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + $session['auth_key']]]]> + + + + + + + + + + + + + + + + + + + + + $key]]]> + $session['auth_key']]]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - methodCallAsyncRead + - $part_one_md5 - $part_one_md5 + + - $dc - $main_dc_id + + - \is_string($dc) + + + + + + + API->dcList[$test][$ipv6]]]> + API->dcList[$test][$ipv6]]]> + API->dcList[$test][$ipv6][$dc_number]]]> + API->dcList[$test][$ipv6][$dc_number]]]> + API->dcList[$test][$ipv6][$dc_number]]]> + API->dcList[$test][$ipv6][$dc_number]['ip_address']]]> + API->dcList[$test][$ipv6][$dc_number]['port']]]> + API->dcList[$test][$ipv6][$dc_number]['secret']]]> + API->dcList[$test][$ipv6][$dc_number]['tcpo_only']]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - $default[2] - $default[2] + + - $API - $connectionsPromise - $datacenter + + + + + + + + + + + + + + - bind + hasTempAuthKey() @@ -193,214 +498,121 @@ permAuthKey =& $connection->permAuthKey]]> - - - ttl[$index])]]> - - - $cacheTtl - - - - Traversable - Traversable - - - - - $index - $index - $index - $index - - - - - $index - - - - - query("SHOW VARIABLES LIKE 'max_connections'")->fetchRow()['Value']]]> - - - - - $settings - $settings - $settings - - - DriverArray - - - $v - $value - $value - - - $v]]> - json_decode($value, true, 256, JSON_THROW_ON_ERROR)]]> - - $dbSettings - $dbSettings - $dbSettings - $dbSettings - $deserializer - $deserializer - $deserializer - $deserializer - $serializer - $serializer - $serializer - $serializer - $table - $table - $table - $table + - - $old - - - setSettings - - - new static() - + + + - - MemoryArray - MemoryArray - - - $array - $flags - - - $settings - - - - - dbSettings]]> - - - $stmt - - - $pdo - - - - - $v - $v - $v - $v - $v - $v - $v - $value - - - igbinary_unserialize(hex2bin($v))]]> - unserialize(hex2bin($v))]]> - json_decode(hex2bin($value), true, 256, JSON_THROW_ON_ERROR)]]> - - - - - dbSettings]]> - - - $v - $v - $v - $v - $v - $value - - - $v]]> - json_decode($value, true, 256, JSON_THROW_ON_ERROR)]]> - - - - - dbSettings]]> - - - $db - - - db->get($key)]]> - $value - - - - - $stmt - - - $db - $db - $db - - - fetchRow()['count']]]> - + + + + + + + + + + + + + + + + - $builtUri - $uri + + - - $webSocketConnector - + + + - $orig[1][0] === BufferedRawStream::class + - - Rfc6455Connector - - $class + + - - wrapper->getAPI()]]> - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + periodicLoops]]> + ]]> + getParameters()[0]->getType()]]> - getDbPrefix - rethrowInner - rethrowInner + + + - self::$pluginCache + - array_values + startedInternal]]> startedInternal]]> - require $file + - new $class + @@ -419,34 +631,236 @@ $result )]]> + + + + + + + + + + methodCallAsyncRead( + 'stories.getPeerStories', + [ + 'peer' => $this->senderId, + ] + )['stories']]]> + methodCallAsyncRead( + 'stories.getStoriesByID', + [ + 'peer' => $this->senderId, + 'id' => array_column($result, 'id'), + ] + )['stories']]]> + + + + + + + + + + + + + + + + + + + + + + + getClient()->methodCallAsyncRead( + DialogId::isSupergroupOrChannel($this->chatId) ? 'channels.getMessages' : 'messages.getMessages', + [ + 'channel' => $this->chatId, + 'id' => [['_' => 'inputMessageReplyTo', 'id' => $this->id]], + ] + )['messages']]]> + + + + + + + + + + chatId]]> + date]]> + id]]> + mentioned]]> + out]]> + replyToMsgId]]> + replyToMsgId]]> + replyToMsgId]]> + replyToMsgId]]> + replyToScheduled]]> + senderId]]> + silent]]> + threadId]]> + threadId]]> + topicId]]> + topicId]]> + ttlPeriod]]> + + + getClient()->methodCallAsyncRead( + 'channels.readHistory', + [ + 'peer' => $this->chatId, + 'channel' => $this->chatId, + 'max_id' => $readAll ? 0 : $this->id, + ] + )]]> + getClient()->methodCallAsyncRead( + 'contacts.block', + [ + 'id' => $this->senderId, + ] + )]]> + getClient()->methodCallAsyncRead( + 'contacts.unblock', + [ + 'id' => $this->senderId, + ] + )]]> + getClient()->methodCallAsyncRead( + 'messages.setTyping', + [ + 'peer' => $this->senderId, + 'top_msg_id' => $this->topicId, + 'action' => $action, + ] + )]]> + getClient()->methodCallAsyncRead( + 'messages.togglePeerTranslations', + [ + 'peer' => $this->chatId, + 'disabled' => false, + ] + )]]> + getClient()->methodCallAsyncRead( + 'messages.togglePeerTranslations', + [ + 'peer' => $this->chatId, + 'disabled' => true, + ] + )]]> + - DialogSetTTL - DialogSetTTL + + ]]> wrapMessage($client->extractMessage($result))]]> wrapMessage($client->extractMessage($result))]]> + + + + + + + + + + + + + + + + + + + + + + + + + + getName()]]]> + closeDate]]> + closePeriod]]> + closed]]> + id]]> + question]]> + recentVoters]]> + totalVoters]]> + + - AbstractPrivateMessage - AbstractPrivateMessage + + + + + + + id]]> + getIdInternal($rawStory['peer'])]]> + + + + + + + + + + + + + + + + + + getName()]]]> + + + + + + + + getName()]]]> + accessHash]]> + description]]> + hash]]> + id]]> + name]]> + title]]> + wrapMedia($rawBotApp['document']) : null]]> wrapMedia($rawBotApp['photo']) : null]]> + + + + + botId]]> + getIdInternal($rawBotCommands['peer'])]]> @@ -457,7 +871,51 @@ )]]> + + + chatInstance]]> + queryId]]> + userId]]> + + + getClient()->methodCallAsyncRead( + 'messages.setBotCallbackAnswer', + [ + 'query_id' => $this->queryId, + 'message' => $message, + 'alert' => $alert, + 'url' => $url, + 'cache_time' => $cacheTime, + ], + )]]> + + + + + + + + + + actorId]]> + chatId]]> + date]]> + userId]]> + viaChatlist]]> + + + + + chatId]]> + forwards]]> + id]]> + + + + id]]> + views]]> + getIdInternal($rawMessageViews)]]> @@ -467,11 +925,63 @@ getIdInternal($rawUpdateChannel)]]> + + + getName()]]]> + + + + + + + + adminId]]> + created]]> + date]]> + expire]]> + limit]]> + link]]> + permanent]]> + requestNeeded]]> + requested]]> + revoked]]> + title]]> + used]]> + + getIdInternal($rawChatInviteRequester['peer'])]]> + + + + + + about]]> + date]]> + userId]]> + + + + + pending]]> + recent]]> + + + + + getName()]]]> + command]]> + description]]> + + + + + ids]]> + + getIdInternal($rawDelete)]]> @@ -484,22 +994,22 @@ - $peerResolved + - $peersResolved + - $peerResolved + - $adminIds + @@ -507,28 +1017,76 @@ API]]> - $API + - $peerResolved + - $matches - $matches - $matches - $matches + + + + + + + + + + + + + + offset]]> + query]]> + queryId]]> + userId]]> + + + + + + + + + + + + - $result + + + + + + + + + + creationDate]]> + keyFingerprint]]> + spoiler]]> + thumb]]> + thumbHeight]]> + thumbWidth]]> + ttl]]> + + + + getClient()->downloadToDir($this, $dir, $cb, $cancellation)]]> + getClient()->downloadToDir($this, $dir, $cb, $cancellation)]]> + getClient()->downloadToFile($this, $file, $cb, $cancellation)]]> + getClient()->downloadToFile($this, $file, $cb, $cancellation)]]> + - array{ + + * }]]> botApiFileId]]> botApiFileUniqueId]]> - $thumbs - $videoThumbs + + + + duration]]> + - AbstractAudio - AbstractAudio + + + + emoji]]> + stickerset]]> + - AbstractSticker - AbstractSticker + + + + duration]]> + height]]> + supportsStreaming]]> + width]]> + - AbstractVideo - AbstractVideo + + + + + + - AnimatedSticker - AnimatedSticker + + + + performer]]> + title]]> + - Audio - Audio + + + + + + + + free]]> + textColor]]> + - CustomEmoji - CustomEmoji + + - Document - Document + + + + + + + + height]]> + width]]> + $t]]]> + - DocumentPhoto - DocumentPhoto + + + + + accessHash]]> + accuracyRadius]]> + lat]]> + long]]> + + + + + + + + $t]]]> + + + getClient()->methodCallAsyncRead( + 'messages.saveGif', + [ + 'id' => $this->botApiFileId, + 'unsave' => false, + ] + )]]> + getClient()->methodCallAsyncRead( + 'messages.saveGif', + [ + 'id' => $this->botApiFileId, + 'unsave' => true, + ] + )]]> + - Gif - Gif + + + + + + + + + + + + + + + x]]> + y]]> + zoom]]> + - MaskSticker - MaskSticker + + + + + + + getName()]]]> + + storyId]]> + viaMention]]> + getIdInternal($rawMedia['peer'])]]> + + hasStickers]]> + - Photo - Photo + + - RoundVideo - RoundVideo + + + + + + - StaticSticker - StaticSticker + + - Sticker - Sticker + + + + + + + $t]]]> + - Video - Video + + + + + + + + duration]]> + - VideoSticker - VideoSticker + + + + waveform]]> + - Voice - Voice + + @@ -660,17 +1335,117 @@ getClient()->wrapMessage($this->getClient()->extractMessage($result))]]> getClient()->wrapMessage($this->getClient()->extractMessage($result))]]> + + + + + + + + + + + + + + + message]]> + message]]> + message]]> + message]]> + message[0] ?? '']]> + protected]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + message[0]]]> + + + + + + + + + + + editDate]]> + forwards]]> + fromScheduled]]> + groupedId]]> + imported]]> + message]]> + protected]]> + psaType]]> + signature]]> + viaBotId]]> + views]]> + + + reactions]]> + + + + $method( + message: $message, + parseMode: $parseMode, + replyMarkup: $replyMarkup, + scheduleDate: $scheduleDate, + noWebpage: $noWebpage, + )]]> + getClient()->methodCallAsyncRead( + 'messages.report', + [ + 'reason' => ['_' => $reason->value], + 'message' => $message, + 'id' => [$this->id], + 'peer' => $this->chatId, + ] + )]]> + - Message - Message + + getClient()->wrapMessage($this->getClient()->extractMessage($result))]]> getClient()->wrapMessage($this->getClient()->extractMessage($result))]]> - $html - $htmlTelegram + + + + + + + + + + + + + + + + methodCallAsyncRead( + 'channels.getParticipant', + [ + 'channel' => $this->chatId, + 'participant' => $member, + ] + )['participant']]]> + getClient()->methodCallAsyncRead( + 'messages.getDiscussionMessage', + ['peer' => $this->chatId, 'msg_id' => $this->id] + )['messages']]]> + + + + + + - ChannelMessage - ChannelMessage + + + + + documentId]]> + + + + + userId]]> + + + + + userId]]> + + + + + getName()]]]> + length]]> + offset]]> + + + + + language]]> + + + + + url]]> + + wrapMessage($client->extractMessage($result))]]> @@ -694,11 +1531,37 @@ wrapMessage($client->extractMessage($result))]]> wrapMessage($client->extractMessage($result))]]> + + + + + + + + + methodCallAsyncRead( + 'channels.getParticipant', + [ + 'channel' => $this->chatId, + 'participant' => $member, + ] + )['participant']]]> + + + + + + + + + + + - DialogTopicCreated - DialogTopicEdited - DialogTopicEdited - DialogTopicEdited + + + + wrapMessage($client->extractMessage($result))]]> @@ -707,23 +1570,29 @@ wrapMessage($client->extractMessage($result))]]> - GroupMessage - GroupMessage + + getClient()->wrapMessage($this->getClient()->extractMessage($result))]]> + + + + + + - DialogScreenshotTaken + getClient()->wrapMessage($this->getClient()->extractMessage($result))]]> - PrivateMessage - PrivateMessage + + @@ -734,42 +1603,279 @@ getClient()->wrapMessage($this->getClient()->extractMessage($result))]]> + + + + + + - DialogScreenshotTaken + getClient()->wrapMessage($this->getClient()->extractMessage($result))]]> - SecretMessage - SecretMessage + + + + + + + + + + + + + attachMenu]]> + domain]]> + + + + + + + + + + + accessHash]]> + callId]]> + + + + + getName()]]]> + + + + + + + + canEdit]]> + date]]> + inviterId]]> + promotedBy]]> + rank]]> + self]]> + userId]]> + + + + + + + + date]]> + kickedBy]]> + left]]> + peer]]> + + + + + + + + rank]]> + userId]]> + + + + + peer]]> + + + + + date]]> + userId]]> + + + + + date]]> + inviterId]]> + userId]]> + viaRequest]]> + + + + + getName()]]]> + + + + + addAdmins]]> + anonymous]]> + banUsers]]> + changeInfo]]> + deleteMessages]]> + editMessages]]> + inviteUsers]]> + manageCall]]> + manageTopics]]> + other]]> + pinMessages]]> + postMessages]]> + + + + + changeInfo]]> + embedLinks]]> + inviteUsers]]> + manageTopics]]> + pinMessages]]> + sendAudios]]> + sendDocs]]> + sendGames]]> + sendGifs]]> + sendInline]]> + sendMedia]]> + sendMessages]]> + sendPhotos]]> + sendPlain]]> + sendPolls]]> + sendRoundvideos]]> + sendStickers]]> + sendVideos]]> + untilDate]]> + viewMessages]]> + + + + ids]]> + pinned]]> + getIdInternal($rawPinned)]]> + + + + + + getName()]]]> + chosen]]> + correct]]> + text]]> + voters]]> + + + + + + + solution]]> + - $html - $htmlTelegram + + + + + + + + + + + + getName()]]]> + + + + + chats]]> + + + + + users]]> + + + + + chats]]> + + + + + users]]> + + + + + + + + getName()]]]> + + + + + + + + + + + + messageId]]> + - Message - Message + + getIdInternal($rawCallback['peer'])]]> + + + gameShortName]]> + + + + + + + + + + + + + + + + + + + + + + + + rawId]]> + + nextSent]]> @@ -779,9 +1885,125 @@ wrapUpdate($update)]]> wrapUpdate($update)]]> + + + + + + protected]]> + + + + + + methodCallAsyncRead('stories.getStoriesByID', ['peer' => $rawStory['peer'], 'id' => [$rawStory['story']['id']]])['stories']]]> + methodCallAsyncRead( + 'stories.sendReaction', + [ + 'add_to_recent' => $recent, + 'peer' => $this->senderId, + 'story_id' => $this->id, + 'reaction' => \is_int($reaction) + ? ['_' => 'reactionCustomEmoji', 'document_id' => $reaction] + : ['_' => 'reactionEmoji', 'emoticon' => $reaction], + ] + )['updates']]]> + methodCallAsyncRead( + 'stories.sendReaction', + [ + 'add_to_recent' => $recent, + 'peer' => $this->senderId, + 'story_id' => $this->id, + ] + )['updates']]]> + + + + + + + + + + + + + + + + + + + + + + + + + + getClient()->methodCallAsyncRead( + 'stories.exportStoryLink', + [ + 'peer' => $this->senderId, + 'id' => $this->id, + ] + )['link']]]> + + + + + + + + + + caption]]> + closeFriends]]> + contacts]]> + date]]> + edited]]> + expireDate]]> + pinned]]> + protected]]> + public]]> + reactionCount]]> + recentViewers]]> + selectedContacts]]> + sentReaction]]> + views]]> + + + + + + + getClient()->methodCallAsyncRead( + 'stories.exportStoryLink', + [ + 'peer' => $this->senderId, + 'id' => $this->id, + ] + )['link']]]> + getClient()->methodCallAsyncRead( + 'stories.incrementStoryViews', + [ + 'peer' => $this->senderId, + 'id' => [$this->id], + ] + )]]> + getClient()->methodCallAsyncRead( + 'stories.report', + [ + 'peer' => $this->senderId, + 'id' => [$this->id], + 'reason' => ['_' => $reason->value], + 'message' => $message, + ] + )]]> + - StoryReaction - StoryReaction + + wrapMessage($client->extractMessage($result))]]> @@ -789,11 +2011,11 @@ wrapUpdate($update)]]> - $first + - $html - $htmlTelegram + + wrapMedia($rawStory['media'], $this->protected)]]> @@ -801,21 +2023,112 @@ + + + + + id]]> + reaction]]> + getIdInternal($rawStory)]]> + + + + + + userId]]> + + + + + chatId]]> + + + + + + + + chatId]]> + + + + topicId]]> + getIdInternal($rawTyping)]]> + + + getName()]]]> + + + + blocked]]> + stories]]> + getIdInternal($rawPeerBlocked)]]> + + + date]]> + stopped]]> + userId]]> + + + + + number]]> + userId]]> + + + + + + + + userId]]> + + + + + emojiId]]> + until]]> + + + + + + + + wasOnline]]> + + + + + + + + expires]]> + + + + + + + firstName]]> + lastName]]> + userId]]> + new UsernameInfo($username), @@ -823,40 +2136,99 @@ )]]> + + + getName()]]]> + active]]> + username]]> + + + + + + + + getName()]]]> + accessHash]]> + creator]]> + dark]]> + default]]> + id]]> + pattern]]> + uniqueId]]> + wrapMedia($rawWallpaper['document'])]]> + + + getName()]]]> + backgroundColor]]> + blur]]> + fourthBackgroundColor]]> + intensity]]> + motion]]> + rotation]]> + secondBackgroundColor]]> + thirdBackgroundColor]]> + + - $code - $errfile - $errline - $errno - $errstr - $file - $line - $message + + + + + + + + + + + + + + + + + + file]]> + file]]> + + + file]]> + line]]> + + + line]]> + - __invoke + + + + + + + + - $id + - self::$map - self::$map ??= new WeakMap - self::$map ??= new WeakMap + + - $id - $id + + @@ -864,11 +2236,23 @@ wrapper->getAPI()->getEventHandler($class)]]> - T|EventHandlerProxy|__PHP_Incomplete_Class|null + - $data + + + wrapper->getAPI()->downloadToDir($messageMedia, $dir, $cb, $cancellation)]]> + wrapper->getAPI()->downloadToDir($messageMedia, $dir, $cb, $cancellation)]]> + wrapper->getAPI()->downloadToFile($messageMedia, $file, $cb, $cancellation)]]> + wrapper->getAPI()->downloadToFile($messageMedia, $file, $cb, $cancellation)]]> + wrapper->getAPI()->uploadFromCallable($callable, $size, $mime, $fileName, $cb, $seekable, $encrypted, $cancellation)]]> + wrapper->getAPI()->uploadFromCallable($callable, $size, $mime, $fileName, $cb, $seekable, $encrypted, $cancellation)]]> + wrapper->getAPI()->uploadFromTgfile($media, $cb, $encrypted, $cancellation)]]> + wrapper->getAPI()->uploadFromTgfile($media, $cb, $encrypted, $cancellation)]]> + wrapper->getAPI()->uploadFromUrl($url, $size, $fileName, $cb, $encrypted, $cancellation)]]> + wrapper->getAPI()->uploadFromUrl($url, $size, $fileName, $cb, $encrypted, $cancellation)]]> + account ??= new \danog\MadelineProto\Namespace\AbstractAPI('account')]]> auth ??= new \danog\MadelineProto\Namespace\AbstractAPI('auth')]]> @@ -877,6 +2261,7 @@ chatlists ??= new \danog\MadelineProto\Namespace\AbstractAPI('chatlists')]]> contacts ??= new \danog\MadelineProto\Namespace\AbstractAPI('contacts')]]> folders ??= new \danog\MadelineProto\Namespace\AbstractAPI('folders')]]> + fragment ??= new \danog\MadelineProto\Namespace\AbstractAPI('fragment')]]> help ??= new \danog\MadelineProto\Namespace\AbstractAPI('help')]]> langpack ??= new \danog\MadelineProto\Namespace\AbstractAPI('langpack')]]> messages ??= new \danog\MadelineProto\Namespace\AbstractAPI('messages')]]> @@ -884,6 +2269,7 @@ phone ??= new \danog\MadelineProto\Namespace\AbstractAPI('phone')]]> photos ??= new \danog\MadelineProto\Namespace\AbstractAPI('photos')]]> premium ??= new \danog\MadelineProto\Namespace\AbstractAPI('premium')]]> + smsjobs ??= new \danog\MadelineProto\Namespace\AbstractAPI('smsjobs')]]> stats ??= new \danog\MadelineProto\Namespace\AbstractAPI('stats')]]> stickers ??= new \danog\MadelineProto\Namespace\AbstractAPI('stickers')]]> stories ??= new \danog\MadelineProto\Namespace\AbstractAPI('stories')]]> @@ -892,69 +2278,144 @@ users ??= new \danog\MadelineProto\Namespace\AbstractAPI('users')]]> - setWrapper - setWrapper - setWrapper - setWrapper - setWrapper - setWrapper - setWrapper - setWrapper - setWrapper - setWrapper - setWrapper - setWrapper - setWrapper - setWrapper - setWrapper - setWrapper - setWrapper - setWrapper - setWrapper - setWrapper + + + + + + + + + + + + + + + + + + + + + + - $class + - downloadToCallable - downloadToDir - downloadToFile - methodCallAsyncRead - uploadFromCallable - uploadFromTgfile - uploadFromUrl + + + + + + + + + + + + + + + __call(...)]]> + + + + + + + + + + + + + + + + + + + + - - getException()]]> - - - getException();]]> - + + + + + + requests[$id]]]> + + + + + + + + + - logger + - $logger - $server + + - - - exception ? $this->exception->getException() : null]]> - - - ?Throwable - + + + + + + + + + + + + getName()][$prop->getName()]]]> + + + + + + + + API]]> + + + API ??= Client::giveInstanceBySession($this->session)]]> + API ??= Client::giveInstanceBySession($this->session)]]> + + + + + + + + + + + + + + + + + + + @@ -962,80 +2423,138 @@ - $address + - (int) $port + + + + + + + + + + - $arguments - $arguments - $arguments + + + - include $autoloadPath + + + + + + + + + - $callback - $server + + - $payload === self::SHUTDOWN - $payload === self::SHUTDOWN + + - unwrap + - ServerCallback - ServerCallback + + - $args + $this->__call($id, $args)]]> - $data + - copy - unwrap + + + + callbacks[$payload[0]]]]> + + + + remoteId]]> + + + + + callbacks]]> + - $payload[0] - $payload[0] + + - $data - Wrapper - Wrapper + + + - new $class($this, $ids) + callbackIds[] = &$callback]]> callbackIds[] = &$callback]]> + + + __call('isRequested')]]> + __call('unsubscribe', [$callback])]]> + + - IteratorAggregate + + + + + + + __call('isReadable')]]> + + + + + __call('isWritable')]]> + + + + + {$propName}->getArrayCopy()]]> + + + + + + bindTo($this, $property->getDeclaringClass()->getName())($property->getName())]]> + @@ -1043,15 +2562,32 @@ + + stdoutUnbuffered->getResource()]]> + + + + stdoutUnbuffered]]> + + + + colors[$level]]]> + - $optional - $optional - $optional - $optional - $optional - $optional - $optional - $optional + + + + + + + + getResource()]]> mode === self::FILE_LOGGER ? $this->optional @@ -1059,22 +2595,28 @@ optional]]> optional]]> optional]]> - stdoutUnbuffered->getResource()]]> - $optional + + + + + - $colors - $stdout - $stdoutUnbuffered + + + + + + - $promise + colors]]> @@ -1089,21 +2631,46 @@ + + + + + + + + + + connection->new_outgoing[$message_id]]]> + connection->outgoing_messages[$message_id]]]> + connection->outgoing_messages[$message_id]]]> + + + + + + + + + getMsgId()]]> + + getSent()]]> + getSent()]]> + API->settings]]> - getRpc + - CheckLoop - CheckLoop - CheckLoop - CheckLoop - CheckLoop + + + + + API]]> @@ -1111,11 +2678,11 @@ - CleanupLoop - CleanupLoop - CleanupLoop - CleanupLoop - CleanupLoop + + + + + connection->msgIdHandler?->cleanup()]]> @@ -1126,186 +2693,423 @@ - HttpWaitLoop + - getSettings + - PingLoop - PingLoop - PingLoop - PingLoop + + + + shared]]> - - getReason(), ' ')]]> - - - throw $e; - + + + + + + + + + + + + + + + + + + + + bufferRead($payload_length)]]> bufferRead(4)]]> bufferRead(4)]]> + bufferRead(4)]]> bufferRead(8)]]> - $message_data - $message_data - $message_key + + + + + + - getReadBuffer + + - ReadLoop + - - $e - $e - $e - ClosedException - + + + + + + + + + + + + + + + + + + + getSerializedBody()]]> getSerializedBody()]]> + + getSerializedBody()]]> + connection->session_id]]> + - getWriteBuffer - getWriteBuffer + + - WriteLoop + connection->isHttp() && empty($this->connection->pendingOutgoing)]]> + connection->pendingOutgoing)]]> $callable()]]> + + $callable()]]> + + + + + + + API->logger)]]> - $msg + - $messages + - feed - feedSingle + + + + + + + + + + + + + + + + + + + + + + feedSingle($update)]]]> + API->feeders[$channelId]]]> + + + API->feeders[$channelId]]]> + + + + + + + + + + state]]> + + + + + + + + + + - checkPts - pts - resume - resume + + + + + + + + + + + + + + + + + + + + + pendingWakeups]]> + state]]> + + + + feeder->feed($updates['updates'])]]> + + - checkSeq - feed + + API->feeders[$channelId]]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + API->authorization['user']['bot']]]> + + + + + channelId]]]> + channelId]]]> + channelId]]]> + channelId]]]> + + + API->feeders[$channelId]]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + pts()]]> + pts()]]> + pts() + 1]]> + feeder->feed($difference['other_updates'])]]> + feeder->feed($difference['other_updates'])]]> + feeder->feed($difference['other_updates'])]]> + + + + + - date - pts - pts - pts - pts - pts - qts - qts - qts - update - update - update - update - update - update - update + + + + + + + + + + + + + + + + API->authorization['user']]]> API->authorization['user']['bot']]]> - $difference - $difference - $difference - $difference - $difference - $difference - $difference - $difference - $difference - $difference - $difference - $difference - $difference - $difference - $difference - $difference - $difference - $difference - $difference - $difference - $difference - $difference - $difference - $difference - $difference - $difference - $difference - $difference - $difference - $difference - $difference - $difference - $difference - $difference - $difference - $difference + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + API->feeders[$channelId]?->resume()]]> + + + + + + dequeue()]]> + + + + channels_state]]> + channels_state]]> + channels_state]]> + datacenter)]]> TL)]]> settings)]]> settings)]]> - new DataCenter($this) - new PeerDatabase($this) + + - $callbacks - $callbacks + + $this->getDownloadInfo(...), 'InputPeer' => $this->getInputPeer(...), - 'InputDialogPeer' => fn (mixed $id): array => ['_' => 'inputDialogPeer', 'peer' => $this->getInputPeer($id)], - 'InputCheckPasswordSRP' => fn (string $password): array => (new PasswordCalculator($this->methodCallAsyncRead('account.getPassword', [], $this->authorized_dc)))->getCheckPassword($password), + 'InputDialogPeer' => $this->getInputDialogPeer(...), + 'InputCheckPasswordSRP' => $this->getPasswordSRP(...), ], )]]> - array + - serializeSession + + + + + + + TL]]> + TL]]> + methodCallAsyncRead('account.getPassword', [], $this->authorized_dc)]]> + methodCallAsyncRead('phone.getCallConfig', [])]]> + settings]]> + settings->getSchema()]]> + settings->getSerialization()->applyChanges()->getInterval()]]> + + + + + release(...)]]> + internalInitDbProperties(...)]]> + internalSaveDbProperties(...)]]> + + + + + + + + + + + + + + authorization['user']['bot']]]> + authorization['user']['bot']]]> + authorization['user']['bot']]]> + authorization['user']['id']]]> + methodCallAsyncRead('help.getCdnConfig', [], $this->authorized_dc))['public_keys']]]> + methodCallAsyncRead('users.getUsers', ['id' => [['_' => 'inputUserSelf']]]))[0]]]> + + + + + + + + + + + + + + + + + + + + + + + + config]]> + configLoop]]> + datacenter]]> + peerDatabase]]> + phoneConfigLoop]]> + referenceDatabase]]> + reportMutex]]> + seqUpdater]]> + serializeLoop]]> + supportUser]]> + tmpDbPrefix]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + datacenter]]> + + + authorization['hint']]]> + authorization['hint']]]> + authorization['user'] ?? false]]> + authorization['user'] ?? false]]> + authorization['user']['bot']]]> + authorization['user']['bot']]]> + config]]> + config]]> + dcList[$test][$ipv6][$dc]['cdn'] ?? false]]> + dcList[$test][$ipv6][$dc]['cdn'] ?? false]]> + getSelf()['premium']]]> + + + cdn_rsa_keys]]> + ]]> + + authorization['hint']]]> authorization['user']['bot']]]> - - channels_state->get()]]> - channels_state->get()]]> - channels_state->get()]]> - channels_state->get()]]> - - $path + getSelf()['bot']]]> + getSelf()['id']]]> getSelf()['premium']]]> authorized_dc]]> + + authorization['hint']]]> authorization['hint']]]> authorization['user']]]> authorization['user']]]> @@ -1368,9 +3314,10 @@ authorization['user']['id']]]> - logger - start - start + + + + @@ -1386,41 +3333,79 @@ authorized === API::LOGGED_IN && \is_int($dc_id)]]> datacenter]]> peerDatabase]]> - \is_int($dc_id) + datacenter)]]> - memprof_dump_pprof($file) - memprof_enabled() + + - - full_chats]]> - settings->getAppInfo()->getLangCode()]]]> + + + + + + + + + + + + + + authKey]]> + id]]> + serverSalt]]> + + + + + + content['_']]]]> + + + content['_']]]> + content[self::RESPONSE_ID_MAP[$this->content['_']]]]]> + + - getBody + + + + state |= self::STATE_ACKED]]> state |= self::STATE_REPLIED]]> state |= self::STATE_SENT]]> + + sent]]> + + + + authKey]]> + + + + authKey]]> - authorized + {$key} =& $bound->{$key}]]> @@ -1430,37 +3415,258 @@ new_outgoing]]> + + getSent()]]> + getSent()]]> + getSent()]]> + + + + + + + + + + + + + + + time_delta]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + time_delta]]> + + + + + + + + + + + + + + + + + + + + fp]]> + $f->await()]]> - $abstractionQueueMutex + - methodCallAsyncRead + + + + + + + + + + + + + API->parseMode($args)['message']]]> + + + + + + + + + + + outgoing_messages[$message_id]]]> + outgoing_messages[$message_id]]]> + + + + + + + + + + + + + + + outgoing_messages[$message_id] ?? $message_id]]> + - release - resume + + - getMaxId + + + + + + + + + + + + + + incoming_messages[$content['answer_msg_id']]]]> + incoming_messages[$content['answer_msg_id']]]]> + incoming_messages[$msg_id]]]> + incoming_messages[$msg_id]]]> + outgoing_messages[$content['msg_id']]]]> + outgoing_messages[$msg_id]]]> + + + + + + + + + + + + + + + + + + getContent()['_']]]> + read()['msg_ids']]]> + read()['server_salt']]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + API->getTL()->getConstructors()->findByPredicate($message->getContent()['_'])['type']]]> + API->getTL()->getConstructors()->findByPredicate($response['_'])['type']]]> + + + incoming_messages[$msg['msg_id']]]]> + incoming_messages[$referencedMsgId]]]> + + + + + + + + + + + + + + + API->authorization['user']['phone']]]> + API->authorization['user']['phone']]]> + - $response - time() + $seconds + + - $msgId - $msgId - $msgId - $msgId + + + + getMsgId()]]> @@ -1472,21 +3678,50 @@ - $msgIdHandler - $new_incoming + + + + + + + getSent()]]> + + + + + + + + + + + + + + + + + + dh_config]]> + - methodCallAsyncRead + - - - syncLoading - - + + + + + + + + + + @@ -1496,11 +3731,14 @@ getSelf()['id']]]> - $media + extractBotAPIFile($this->MTProtoToBotAPI($media))['file_id']]]> + + + @@ -1509,37 +3747,331 @@ - - - - array + - $data - $media + + - genAllFile + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + methodCallAsyncRead('upload.getCdnFileHashes', ['file_token' => $file, 'offset' => $offset, 'cancellation' => $cancellation], $this->authorized_dc)]]> + methodCallAsyncRead('upload.reuploadCdnFile', ['file_token' => $messageMedia['file_token'], 'request_token' => $res['request_token'], 'cancellation' => $cancellation], $this->authorized_dc)]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + TL->getConstructors()->findByPredicate($media['_'])['type']]]> + + + + + + + + + + + + + + + + + + + + + + + cdn_hashes[$file][$hash['offset']]]]> + TL->getConstructors()->findByPredicate($media['_'])['type'] => $media]]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + cdn_hashes]]> + cdn_hashes]]> + + + write[$offset]->getFuture()->await($this->cancellation)]]> + + + + read[$offset]]]> write[$offset]]]> write[$offset]]]> - $datacenter - $messageMedia + + + + + + + + - decrypt - getReference - getReference - getReference - getReference - getReference - getReference - getReference + + + + + + + + @@ -1550,32 +4082,28 @@ - $res - $res - $res - $res - $res - $res - $res - $res - $res - $res - $res - $res - $res + + + + + + + + + + + + + - - - - - $datacenter + - $cb(100, $speed, $time) + @@ -1583,63 +4111,288 @@ + + methodCallAsyncRead( + $method, + $params + )]]> + + + + + + + + + - Message + - $file - $file + + - static function (string $payload, int $offset) use ($stream, $seekable, $lock) { + + + + + + + + + + + + getFile()]]> + + + + + + + + + getFile()]]> + + + + getHeaders()]]> + + + + + + + + + + + + - $file - $file + + - $body - $size + + + + + + - seek - seek + + - $l + - populateFrom + + + + + pendingDb[$id]]]> + + + + + + cache[$key][$id]]]> + + + + + + + + pendingDb[$id]]]> + + + + + + + + + + + + + + + populateFrom(...))]]> + + + + + + - $id + - $peers - $peers - $peers - $peers + db]]> - $db + + + + + + + + + + + + + toBytes()]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + current_algo]]> + new_algo]]> + srp_id]]> + + + + + + + + + + + + + + + + - powMod + + + + pendingDb]]> + + + + + + + + + + + + + + + + + + API->authorization['user']['id']]]> + API->methodCallAsyncRead('help.getSupport', [])['user']]]> + + API->methodCallAsyncRead('contacts.resolveUsername', ['username' => $username]))['peer']]]> + + + db[$bot_api_id]]]> + db[$chat['id']]]]> + db[$chat['id']]]]> + db[$user['id']]]]> + db[$user['id']]]]> + pendingDb[$user['id']]]]> + pendingDb[$user['id']]]]> + + + + + + + + + + + + $username]]]> + + + + + + + pendingDb]]> + + + getFull($id)['last_update'] ?? 0]]> + getFull($id)['last_update'] ?? 0]]> + + + + + + + API->getIdInternal($full)]]> API->getIdInternal($full)]]> @@ -1655,31 +4408,203 @@ pendingDb]]> - $db - $fullDb - $usernames + + + - (int) $id + + + + + + getInfo($peer, \danog\MadelineProto\API::INFO_TYPE_CONSTRUCTOR)['forum']]]> + + + - fwdPeerIsset - getInputConstructor - getInputPeer + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + TL->getConstructors()->findByPredicate($constructor['_'])['type']]]> + authorization['user']['id']]]> + authorization['user']['id']]]> + authorization['user']['id']]]> + authorization['user']['id']]]> + + + + getIdInternal($participant['peer'])]]]> + + + + getIdInternal($participant['peer'])]]]> + TL->getConstructors()->findByPredicate($constructor['_'])['type'] => $constructor]]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + supportUser]]> + + + + + + + + + + + + + + + + + + + - \danog\MadelineProto\API::PEER_TYPE_* + - $promises + - $full + + getIdInternal($id)]]> getIdInternal($id)]]> peerDatabase->getFull($partial['bot_api_id'])]]> @@ -1701,10 +4626,140 @@ - array_values + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + API->authorization['user']['id']]]> + API->authorization['user']['id']]]> + + + + + + + cache[$key][$location]]]> + cache[$key][$location]]]> + cache[$key][$location]]]> + cache[$key][self::serializeLocation($locationType, $location)]]]> + + + cache[$key][$location]]]> + cache[$key][$location]]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1722,14 +4777,33 @@ getDb($locationString)['origins']]]> - $db + + + + + + + + + + + + + + + + + + + + - $extra_ranges - $range_orig - $seek_end + + + @@ -1741,15 +4815,304 @@ nextSent]]> - $update + - getUpdatesState - loadUpdateState + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + getUpdatesState()]]> + + + + + + + + + + + + + + + + + + + + + rethrowHandler]]> + rethrowHandler]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + TL->getConstructors()->findByPredicate('message')['params']]]> + authorization['user']['id']]]> + authorization['user']['id']]]> + authorization['user']['id']]]> + authorization['user']['id']]]> + authorization['user']['id']]]> + authorization['user']['id']]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + authorization['hint']]]> + + + + + + + + + feeders[FeedLoop::GENERIC]->feedSingle($update)]]]> + calls[$update['phone_call']['id']]]]> + calls[$update['phone_call']['id']]]]> + calls[$update['phone_call']['id']]]]> + calls[$update['phone_call']['id']]]]> + calls[$update['phone_call']['id']]]]> + calls[$update['phone_call']['id']]]]> + calls[$update['phone_call']['id']]]]> + calls[$update['phone_call']['id']]]]> + calls[$update['phone_call']['id']]]]> + calls[$update['phone_call']['id']]]]> + calls[$update['phone_call_id']]]]> + calls[$update['phone_call_id']]]]> + eventHandlerMethods[$updateType]]]> + feeders[$this->feeders[FeedLoop::GENERIC]->feedSingle($update)]]]> + feeders[$this->feeders[FeedLoop::GENERIC]->feedSingle($updates['update'])]]]> + msg_ids[$peer_id]]]> + msg_ids[$peer_id]]]> + secretChats[$update['message']['chat_id']]]]> + secretChats[$update['message']['chat_id']]]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + authorization]]> + authorization]]> + authorized_dc]]> + + + + + + + + + + + + + + + + + + + + + qts()]]> + qts()]]> + qts()]]> + qts()]]> + + + + + + + + + + + + + + calls]]> + calls]]> + getIdInternal($message['action']['from_id'])]]> getIdInternal($message['action']['to_id'])]]> + webhookUrl]]> authorization['user']]]> @@ -1766,139 +5129,630 @@ authorization['user']['id']]]> - addPendingWakeups - waitForInternalStart + + - $first + + eventHandlerHandlers) !== 0 && \is_array($update)]]> - \is_array($update) + + + + + + + + + + + + pts + $update['pts_count'])]]> + + + + + + + + + + + + + + + + + - completeLogin - createApp - getApp - hasApp + + + + + + getHeaders('app')]]> + getHeaders('origin')]]> + getHeaders('origin')]]> + getHeaders('refer')]]> + getHeaders('refer')]]> + getHeaders('refer')]]> + + + + + + + hash]]> + settings]]> - $value + settings]]> + + + - methodCallAsyncRead + - $wrapper + + + + + + + comments]]> + vendorString]]> + + + new('char[1024]'))]]> + + + + + + + + > 3)]]> + + + + + + + opus_encoder_ctl($encoder, self::OPUS_SET_BANDWIDTH_REQUEST, self::OPUS_BANDWIDTH_FULLBAND)]]> + opus_encoder_ctl($encoder, self::OPUS_SET_BITRATE_REQUEST, 130*1000)]]> + opus_encoder_ctl($encoder, self::OPUS_SET_COMPLEXITY_REQUEST, 10)]]> + opus_encoder_ctl($encoder, self::OPUS_SET_INBAND_FEC_REQUEST, 1)]]> + opus_encoder_ctl($encoder, self::OPUS_SET_PACKET_LOSS_PERC_REQUEST, 1)]]> + opus_encoder_ctl($encoder, self::OPUS_SET_SIGNAL_REQUEST, self::OPUS_SIGNAL_MUSIC)]]> + opus_get_version_string()]]> + + + + + + + + + + + + + + + + + streamCount]]> + + + + + + + + + + + + + + + opus_strerror($err)]]> + + + + streamCount]]> + + + ]]> + + + + + + + + + + + + + + + + + currentDuration += $frameDuration]]> + currentDuration += $totalDuration]]> + + + + + type('char*')]]> + + + stream)($headers['number_page_segments'])]]> + stream)(23)]]> + + + + + + + + + + + + + + + + + + + + + + + stream)($headers['number_page_segments'])]]> + stream)($sizeAccumulated)]]> + stream)(23)]]> + stream)(4)]]> + + + cdata]]> + + - $file - $message + + + + + + - $level - $message + + + + + - $previous + - $caller - $code - $message + + + + + + + - $caller - $method + + + + + + + + + + + + + + + + + + + + + + e]]> + n]]> + + + + + + + serializeObject(['type' => 'bytes'], $instance->n->toBytes(), 'key')]]> + - $e - $fp - $n + + + + + + + + + + + + + + + + + + + + getSecretChatController($chatId)->getMessage($randomId)['message']]]> + temp_requested_secret_chats[$params['id']]]]> + + + + + + + + + + secretChats[$chat]]]> + secretChats[$params['id']]]]> + secretChats[$params['id']]]]> + secretChats[$params['id']]]]> + secretChats[\is_array($chat) ? $chat['chat_id'] : $chat]]]> + temp_requested_secret_chats[$params['id']]]]> + temp_requested_secret_chats[$params['id']]]]> + temp_requested_secret_chats[$params['id']]]]> + temp_requested_secret_chats[$res['id']]]]> + + + secretChats[$chat]]]> + secretChats[\is_array($chat) ? $chat['chat_id'] : $chat]]]> + + + + + + + + + + + + + + + + + + + + + + secretChats]]> + secretChats]]> + - SecretMessage + + + + - remoteLayer === 8]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + outgoing[$request['seq']]]]> + outgoing[$request['seq']]]]> + outgoing[$seq]]]> + randomIdMap[$message['message']['decrypted_message']['random_id']]]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + gapEnd]]> + gapQueueSeq]]> + gapQueueSeq]]> + rekeyExchangeId]]> + remoteLayer]]> + remoteLayer]]> + ttl]]> + + + + + + + + + + + + + + + + + gapEnd]]> + - $incoming - $outgoing - $randomIdMap + + + + + + + + + - array{0: (ChannelledSocket|APIWrapper|Throwable|null|0), 1: (callable|null)} + + + + + + + getFuture()]]> + getFuture()]]> + getFuture()]]> + + + + + + + - $ipcSocket - $ipcSocket + + + + + + - $warningId + - + + lightState ??= $this->unserialize($this->lightStatePath)]]> + + unserialize($this->ipcStatePath)]]> - - - ?IpcState - + + + + + + + + + + read(null, 1)]]> read(null, 1)]]> - $php[0] - $php[1] + + - $php[0] - $php[1] + + + + - lightState ??= $this->unserialize($this->lightStatePath)]]> + @@ -1906,12 +5760,26 @@ extra]]> + + + + + + + + + + + + + + - + - $entities + length]]> @@ -1920,113 +5788,370 @@ - $stream + + + bufferRead(4)]]> + + + + + + + + + + + + + + + + + + + + + + + + bufferRead(4))[1]]]> + + + + + stream]]> + stream->getSocket()]]> + - $l + - $append - $append_after - $memory_stream - $stream + + + + + + memory_stream]]> + memory_stream]]> + memory_stream]]> + memory_stream]]> + memory_stream]]> + memory_stream]]> + memory_stream]]> + memory_stream]]> + memory_stream]]> + memory_stream]]> + memory_stream]]> + memory_stream]]> + memory_stream]]> + memory_stream]]> + memory_stream]]> + memory_stream]]> + memory_stream]]> + memory_stream]]> + + + + + + + + + append]]> + append_after]]> + + + stream]]> + stream->getSocket()]]> + stream->read($cancellation)]]> + - $append - $append_after - $decrypt - $encrypt - $extra - $read_buffer - $stream - $write_buffer + + + + + + + + + + extra['decrypt']['iv']]]> + extra['decrypt']['key']]]> + extra['encrypt']['iv']]]> + extra['encrypt']['key']]]> + + + extra['decrypt']]]> + extra['decrypt']]]> + extra['decrypt']['iv']]]> + extra['decrypt']['key']]]> + extra['encrypt']]]> + extra['encrypt']]]> + extra['encrypt']['iv']]]> + extra['encrypt']['key']]]> + + + + + + + + + + + + append]]> + append_after]]> + + + decrypt]]> + encrypt]]> + stream]]> + stream->getSocket()]]> + decrypt->encrypt($this->read_buffer->bufferRead($length, $cancellation))]]> + - $append - $append_after + + - read - write + + - $hash_name - $read_buffer - $read_check_after - $read_check_pos - $read_hash - $rev - $stream - $write_buffer - $write_check_after - $write_check_pos - $write_hash + + + + + + + + + + + + + + + hash_name]]> + hash_name]]> + read_hash]]> + read_hash]]> + read_hash]]> + write_hash]]> + write_hash]]> + write_hash]]> + + + + + + + + + + + + + + + + + + + + read_check_pos]]> + read_check_pos]]> + read_check_pos]]> + write_check_pos]]> + write_check_pos]]> + write_check_pos]]> + + + + + read_buffer->bufferRead($length, $cancellation)]]> + stream]]> + stream->getSocket()]]> + + + + + memory_stream]]> + memory_stream]]> + memory_stream]]> + memory_stream]]> + memory_stream]]> + memory_stream]]> + + + stream]]> + - close + - $obj + - $socketContext - $uri + + - $extra + + + + + + + + + + + + + + + - StreamInterface + - $stream + + + + + + + + + + + + + + + + + + + + + + bufferRead(3)]]> + + + + + + stream]]> + stream->getSocket()]]> + - $l + - $in_seq_no - $out_seq_no - $stream + + + + + bufferRead(4)]]> + bufferRead(4)]]> + + + + + + + + + + + + + + + + + + + + + + + + + in_seq_no]]> + out_seq_no]]> + + + + + stream]]> + stream->getSocket()]]> + - $l + - $stream - $stream - $uri + + + - $code - $ctx - $header + + + + + ctx]]> + ctx]]> + + + + + header]]> + + + code]]> + + + bufferRead(1)]]> + + uri->getPort()]]> + - $code - $current_header[1] - $description + + + getStream($header)]]> @@ -2034,56 +6159,154 @@ - $stream + + + bufferRead(4)]]> + + + + + + + + + + + + + + + + + + stream]]> + stream->getSocket()]]> + - $l + - $stream + + + bufferRead(4)]]> + + + + + + + + + + + + + + + + + + stream]]> + stream->getSocket()]]> + - $l + - $extra - $stream + + + + + + + + + + extra['address']]]> + extra['port']]]> + extra['secret']]]> + extra['secret']]]> + + + extra['address']]]> + extra['port']]]> + extra['secret']]]> + extra['secret']]]> + - $stream + - $extra + - - $read - + + + + + extra['address']]]> + extra['port']]]> + + + + + + + + + + + + + + + + bufferRead(1)]]> + + extra['address']]]> + extra['password']]]> + extra['port']]]> + extra['username']]]> + + + + - $code - $current_header[1] - $description + + + - $l + getStream()]]> + + + + + + + + - $stream + - $extra + - + bufferRead(1)]]> bufferRead(1)]]> bufferRead(1)]]> @@ -2096,68 +6319,327 @@ bufferRead(16)]]> bufferRead(2)]]> bufferRead(4)]]> - $length - + extra['password']]]> + extra['username']]]> + + + extra['address']]]> + extra['password']]]> + extra['port']]]> + extra['username']]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + extra['address']]]> + extra['password']]]> + extra['port']]]> + extra['username']]]> + - $ip + getStream(\chr(5).\chr(\strlen($methods)).$methods)]]> + + + + + + + + + + + + + + + + + + stream]]> + - setupTls + + + stream]]> + - getStream + + + stream]]> + + + + + + stream]]> + - write + - write + - $connector - $stream + + + + + - $data + - stream]]> - stream]]> - stream]]> - ?WebsocketMessage - ClosedException - Rfc6455ConnectionFactory - Rfc6455Connector - WebsocketConnection - WebsocketConnector - WebsocketConnector - WebsocketHandshake + - - WsStream - - - - - WssStream - - $markup + - parseReplyMarkup + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + }>]]> @@ -2174,33 +6656,153 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - $arguments[$key] - $arguments[$key] - $arguments[$key] + + + - $photo - $photoSize - $thumbnail + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + id]]> + - $file - $message + + + + + + - $posClose === false - $posClose === false + + + + + + + + + + + + + + + + + + + + authorization['user']['id']]]> + + + + + + + + + + + + + + + + + + + + + + + + + @@ -2208,6 +6810,9 @@ authorization['user']]]> authorization['user']['id']]]> + + + @@ -2218,87 +6823,575 @@ - $file - $message + + + + + + + + + + + + + + + + + + + + + + + + + + + + - \is_resource($stream) - \is_resource($stream) + + - $arg + - deserialize - serializeMethod - serializeObject - serializeParams + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + toBytes()]]> + + + + + + + + + + outgoing_messages[$x['req_msg_id']]]]> + outgoing_messages[$x['req_msg_id']]->constructor]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + constructors->findByPredicate($object['_'])['type']]]> + constructors->findByPredicate($value['_'])['type']]]> + constructors->findByPredicate($value['_'])['type']]]> + constructors->findByPredicate($value['_'])['type']]]> + constructors->findByPredicate('vector')['id']]]> + constructors->findByPredicate('vector')['id']]]> + constructors->findByPredicate((bool) $object ? 'boolTrue' : 'boolFalse')['id']]]> + constructors->findByType($type)['predicate']]]> + + + outgoing_messages[$x['req_msg_id']]]]> + outgoing_messages[$x['req_msg_id']]]]> + outgoing_messages[$x['req_msg_id']]]]> + outgoing_messages[$x['req_msg_id']]]]> + outgoing_messages[$x['req_msg_id']]]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + afterConstructorDeserialization[$x['_']]]]> + afterConstructorDeserialization[$x['_']]]]> + afterMethodResponseDeserialization[$type['connection']->outgoing_messages[$x['req_msg_id']]->constructor]]]> + afterMethodResponseDeserialization[$type['connection']->outgoing_messages[$x['req_msg_id']]->constructor]]]> + beforeConstructorDeserialization[$x['_']]]]> + beforeConstructorDeserialization[$x['_']]]]> + beforeConstructorSerialization[$object['_']]]]> + beforeConstructorSerialization[$object['_']]]]> + tdDescriptions['constructors'][$name]]]> + tdDescriptions['constructors'][$name]]]> + tdDescriptions['constructors'][$name]]]> + tdDescriptions['constructors'][$name]]]> + tdDescriptions['methods'][$name]]]> + tdDescriptions['methods'][$name]]]> + typeMismatch[$type['type']]]]> + typeMismatch[$type['type']]]]> + outgoing_messages[$x['req_msg_id']]]]> + outgoing_messages[$x['req_msg_id']]]]> + outgoing_messages[$x['req_msg_id']]]]> + outgoing_messages[$x['req_msg_id']]]]> + outgoing_messages[$x['req_msg_id']]]]> + outgoing_messages[$x['req_msg_id']]]]> + outgoing_messages[$x['req_msg_id']]]]> + + + + + + + + + afterConstructorDeserialization[$x['_']]]]> + afterConstructorDeserialization[$x['_']]]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + secretLayer]]> + secretLayer]]> + + + + + + + + + + + + + + + $flag, + 'name' => $name, + 'type' => $type, + 'pow' => $pow + ]]]> + + + + + + + + + + + + + + + + + + + + + constructors->findByPredicate('vector')['id']]]> + serializeObject($current_argument, $arguments['file']['mime_type'], $name, $layer)]]> + serializeObject($current_argument, $value, $name, $layer)]]> + serializeObject(['type' => $type['subtype']], $current_object, $k, $layer)]]> + serializeObject(['type' => $type['subtype']], $current_object, $k, $layer)]]> + serializeObject(['type' => 'bytes'], Tools::random(15 + 4 * Tools::randomInt(modulus: 3)), 'random_bytes')]]> + + + + + + + + + + + + + outgoing_messages]]> + outgoing_messages]]> + outgoing_messages[$x['req_msg_id']]->constructor]]> + outgoing_messages[$x['req_msg_id']]->constructor]]> + outgoing_messages[$x['req_msg_id']]->constructor]]> + - $value + - $value - $value - $value + + + - $value + - botAPIToMTProto + - $elem[1] - $elem[1] - $elem[1] - $elem[1] + + + + - $afterConstructorDeserialization - $afterMethodResponseDeserialization - $beforeConstructorDeserialization - $beforeConstructorSerialization - $beforeMethodResponseDeserialization - $constructors - $methods - $tdDescriptions - $typeMismatch + + + + + + + + + - findByPredicate - findByType + + + + + + + + + by_id[$json_dict['id']]['layer']]]> + + + + + + + by_id[$json_dict['id']]['layer']]]> + + + by_id[$chosenid]]]> + by_id[$json_dict['id']]]]> + by_id[$json_dict['id']]]]> + by_id[$json_dict['id']]]]> + by_id[$json_dict['id']]]]> + by_id[$json_dict['id']]]]> + by_id[$this->by_predicate_and_layer[$predicate]]]]> + layers[$json_dict['layer']]]]> + + + + + + + + + + + + + + + + + + + + + + + + - deserialize - serializeMethod - serializeObject + + + - findById - findByMethod + + + + + + + + + + + + by_id[$json_dict['id']]['subtype']]]> + by_id[$json_dict['id']]['type']]]> + + + by_id[$json_dict['id']]]]> + by_id[$json_dict['id']]]]> + by_id[$json_dict['id']]]]> + by_id[$this->by_method[$method_name]]]]> + by_method[$json_dict['method']]]]> + + + + + + + + + + + + + + + + + + + + + + + by_id[$key]['params']]]> + + + + + + + + + + + + + + + + + by_id[$key]['flags']]]> + by_id[$key]['flags'][$kkey]]]> + by_id[$key]['params']]]> + by_id[$key]['params'][$kkey]]]> + + + by_id[$key]['flags'][$kkey]]]> + by_id[$key]['params'][$kkey]]]> + + + + + + + + - click + + + authorization['user']['id']]]> + + + button[$name]]]> + button[$name]]]> + button[$name]]]> + button[$name]]]> + + + id]]> + label]]> + peer]]> + peer]]> + authorization['user']]]> authorization['user']['id']]]> @@ -2306,18 +7399,58 @@ - $val + $this->{$var}]]> - getVar + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + > 21]]> + + + + + + - $value - $value + + + + + $this->{$var}, @@ -2333,28 +7466,142 @@ )->__invoke()]]> - __invoke - __invoke + + - $buffer === null + {$var} =& $val]]> + + callID]]> + date]]> + otherID]]> + otherID]]> + getSelf()['id']]]> + + + + subtract(Magic::$two)]]> + + + + + methodCallAsyncRead('phone.requestCall', [ + 'user_id' => $user, + 'g_a_hash' => hash('sha256', $g_a->toBytes(), true), + 'protocol' => VoIPController::CALL_PROTOCOL, + ])['phone_call']]]> + + + + + + + calls[$res['id']]]]> + + + + + + + + + + + + calls]]> + + udp]]> - $stream + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -2366,8 +7613,8 @@ - $crc - $encrypted_data + + bufferRead(4)]]> bufferRead(4)]]> bufferRead(4)]]> @@ -2376,12 +7623,17 @@ bufferRead(4)]]> bufferRead(8)]]> + + + + bufferRead(4)]]> + - getReadBuffer + - $ack_mask - $out_seq_no + + getStream()]]> @@ -2390,17 +7642,83 @@ udp]]> - true + - ClosedException - ClosedException + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - readLength(1))]]> - authKey]]> authKey]]> @@ -2409,28 +7727,154 @@ call]]> - $visualization - $visualization + + + + + + + + + + + + subtract(Magic::$two)]]> + + + + + + + + + + call['a']]]> + call['b']]]> + + readLength(2))[1]]]> + + + + + + + + + + + + + + + + API->methodCallAsyncRead('phone.confirmCall', [ + 'key_fingerprint' => substr(sha1($key, true), -8), + 'peer' => ['id' => $params['id'], 'access_hash' => $params['access_hash'], '_' => 'inputPhoneCall'], + 'g_a' => $this->call['g_a'], + 'protocol' => self::CALL_PROTOCOL, + ]))['phone_call']]]> + + + + + + + + + + + $data]]]> + + + + + + + + + + + + + pendingPing]]> - - call['g_a']]]> - - $authKey - $bestEndpoint - $messageHandler + + + API->logger)]]> + + + + + + + + + + + + + + + + methodCallAsyncRead(...)]]> + + ]]> + + + + + + + + + + + + + + + + + + authorization['user']['bot']]]> + authorization['user']['bot']]]> + authorization['user']['bot']]]> + botDialogsUpdatesState['pts']]]> + methodCallAsyncRead( + 'updates.getDifference', + $state + ['cancellation' => Tools::getTimeoutCancellation(15.0), 'floodWaitLimit' => 86400] + )['_']]]> + + + + + + + + + + + + + + botDialogsUpdatesState]]> + botDialogsUpdatesState]]> + bottomPts]]> + topPts]]> + authorization['user']]]> authorization['user']]]> @@ -2440,7 +7884,7 @@ authorization['user']['bot']]]> - $dialogs + cachedAllBotUsers]]> @@ -2452,8 +7896,11 @@ event_handler_instance === null || $this->event_handler_instance instanceof __PHP_Incomplete_Class]]> event_handler_instance instanceof __PHP_Incomplete_Class]]> + + + - PluginEventHandler|EventHandlerProxy|null + pluginInstances[$class]]]> @@ -2463,13 +7910,65 @@ pluginInstances[$class]]]> - new $class_name + - array + + + + + + + + + methodCallAsyncRead( + 'auth.importBotAuthorization', + [ + 'bot_auth_token' => $token, + 'api_id' => $this->settings->getAppInfo()->getApiId(), + 'api_hash' => $this->settings->getAppInfo()->getApiHash(), + ], + )]]> + methodCallAsyncRead('account.getPassword', [])]]> + methodCallAsyncRead('auth.signUp', ['phone_number' => $this->authorization['phone_number'], 'phone_code_hash' => $this->authorization['phone_code_hash'], 'phone_code' => $this->authorization['phone_code'], 'first_name' => $first_name, 'last_name' => $last_name])]]> + + + + + + + + + + + + + + authorization['hint']]]> + authorization['hint']]]> + authorization['phone_number']]]> + + + + + + + + + authorization]]> + authorization]]> + authorization]]> + authorization]]> + authorization]]> + authorization]]> + authorized_dc]]> + + + + authorization['phone_code']]]> authorization['phone_code_hash']]]> @@ -2478,12 +7977,12 @@ authorization['phone_number']]]> - \is_array($auth_key) + - loop + @@ -2494,7 +7993,7 @@ getAuthorization() === \danog\MadelineProto\API::LOGGED_IN]]> - array + @@ -2503,15 +8002,10 @@ - $title - - - - - $result - $result + + diff --git a/psalm.xml b/psalm.xml index 424bf1803..d8ae6a560 100644 --- a/psalm.xml +++ b/psalm.xml @@ -1,6 +1,6 @@ props = $props; } - public function getException(): object + public function getException(): \Throwable { $prev = new MadelineProtoException("Client backtrace"); diff --git a/src/Lang.php b/src/Lang.php index 088e062c1..74f49bc72 100644 --- a/src/Lang.php +++ b/src/Lang.php @@ -1550,6 +1550,7 @@ If you intentionally deleted this account, ignore this message.', ]; // THIS WILL BE OVERWRITTEN BY $lang["en"] + /** @var array */ public static array $current_lang = [ '2fa_uncalled' => 'I\'m not waiting for the password! Please call the phoneLogin and the completePhoneLogin methods first!', 'accepting_call' => 'Accepting call from %s...', diff --git a/src/Loop/Connection/ReadLoop.php b/src/Loop/Connection/ReadLoop.php index ad715d702..fc60cae62 100644 --- a/src/Loop/Connection/ReadLoop.php +++ b/src/Loop/Connection/ReadLoop.php @@ -22,7 +22,7 @@ namespace danog\MadelineProto\Loop\Connection; use Amp\ByteStream\PendingReadError; use Amp\ByteStream\StreamException; -use Amp\Websocket\ClosedException; +use Amp\Websocket\WebsocketClosedException; use danog\Loop\Loop; use danog\MadelineProto\Logger; use danog\MadelineProto\MTProto\MTProtoIncomingMessage; @@ -130,7 +130,7 @@ final class ReadLoop extends Loop } try { $buffer = $this->connection->stream->getReadBuffer($payload_length); - } catch (ClosedException $e) { + } catch (WebsocketClosedException $e) { $this->API->logger($e->getReason()); if (str_starts_with($e->getReason(), ' ')) { $payload = -((int) substr($e->getReason(), 7)); diff --git a/src/Ogg.php b/src/Ogg.php index bd5962d7b..07d931d18 100644 --- a/src/Ogg.php +++ b/src/Ogg.php @@ -543,6 +543,7 @@ final class Ogg } } } + \assert(isset($opus)); $checkErr = static function (int|CData $err) use ($opus): void { if ($err instanceof CData) { $err = $err->cdata; diff --git a/tools/translator.php b/tools/translator.php index 9e12a703e..a5f57ff3a 100644 --- a/tools/translator.php +++ b/tools/translator.php @@ -34,6 +34,7 @@ final class Lang public static array $lang = %s; // THIS WILL BE OVERWRITTEN BY $lang["en"] + /** @var array */ public static array $current_lang = %s; }'; From a2a7c3a7e657641651c9df2f7856a2e1f2ff0ec9 Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Tue, 16 Apr 2024 19:38:03 +0200 Subject: [PATCH 23/24] Small psalm fix --- psalm.xml | 1 + src/MTProtoTools/FilesLogic.php | 1 + 2 files changed, 2 insertions(+) diff --git a/psalm.xml b/psalm.xml index d8ae6a560..c76a77ba7 100644 --- a/psalm.xml +++ b/psalm.xml @@ -20,6 +20,7 @@ + diff --git a/src/MTProtoTools/FilesLogic.php b/src/MTProtoTools/FilesLogic.php index 40c9985a0..c5eb07111 100644 --- a/src/MTProtoTools/FilesLogic.php +++ b/src/MTProtoTools/FilesLogic.php @@ -111,6 +111,7 @@ trait FilesLogic Assert::true(isset($_SERVER['REQUEST_METHOD'])); + /** @psalm-suppress PossiblyUndefinedArrayOffset */ $result = ResponseInfo::parseHeaders( $_SERVER['REQUEST_METHOD'], $headers, From 4f277ad13db5b67f611aacacc4c89a25f071ba42 Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Thu, 18 Apr 2024 20:01:26 +0200 Subject: [PATCH 24/24] Small psalm fix --- psalm.xml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/psalm.xml b/psalm.xml index c76a77ba7..3c327d44e 100644 --- a/psalm.xml +++ b/psalm.xml @@ -31,10 +31,5 @@ - - - - -