diff --git a/examples/bot.php b/examples/bot.php
index 10a18824a..ea22cb369 100755
--- a/examples/bot.php
+++ b/examples/bot.php
@@ -35,7 +35,6 @@ use danog\MadelineProto\EventHandler\Plugin\RestartPlugin;
use danog\MadelineProto\EventHandler\SimpleFilter\FromAdmin;
use danog\MadelineProto\EventHandler\SimpleFilter\Incoming;
use danog\MadelineProto\EventHandler\SimpleFilter\IsReply;
-use danog\MadelineProto\LocalFile;
use danog\MadelineProto\Logger;
use danog\MadelineProto\ParseMode;
use danog\MadelineProto\RemoteUrl;
diff --git a/examples/secret_bot.php b/examples/secret_bot.php
index 5cfef48de..5e8c6b8b0 100755
--- a/examples/secret_bot.php
+++ b/examples/secret_bot.php
@@ -21,14 +21,11 @@ use danog\MadelineProto\EventHandler\Attributes\Handler;
use danog\MadelineProto\EventHandler\Message\PrivateMessage;
use danog\MadelineProto\EventHandler\Message\SecretMessage;
use danog\MadelineProto\EventHandler\SimpleFilter\Incoming;
+use danog\MadelineProto\LocalFile;
use danog\MadelineProto\Logger;
use danog\MadelineProto\Settings;
use danog\MadelineProto\SimpleEventHandler;
-use function Amp\async;
-use function Amp\File\read;
-use function Amp\Future\await;
-
/*
* Various ways to load MadelineProto
*/
@@ -86,234 +83,51 @@ class SecretHandler extends SimpleEventHandler
if (isset($this->sent[$update->chatId])) {
return;
}
- $secret_media = [];
-
- // Photo uploaded as document, secret chat
- $secret_media['document_photo'] = [
- 'peer' => $update->chatId,
- 'file' => 'tests/faust.jpg',
- 'message' => [
- '_' => 'decryptedMessage',
- 'ttl' => 0,
- 'message' => '',
- 'media' => [
- '_' => 'decryptedMessageMediaDocument',
- 'thumb' => read('tests/faust.preview.jpg'),
- 'thumb_w' => 90,
- 'thumb_h' => 90,
- 'mime_type' => mime_content_type('tests/faust.jpg'),
- 'caption' => 'This file was uploaded using MadelineProto',
- 'file_name' => 'faust.jpg',
- 'size' => filesize('tests/faust.jpg'),
- 'attributes' => [
- [
- '_' => 'documentAttributeImageSize',
- 'w' => 1280,
- 'h' => 914,
- ],
- ],
- ],
- ],
- ];
// Photo, secret chat
- $secret_media['photo'] = [
- 'peer' => $update->chatId,
- 'file' => 'tests/faust.jpg',
- 'message' => [
- '_' => 'decryptedMessage',
- 'ttl' => 0,
- 'message' => '',
- 'media' => [
- '_' => 'decryptedMessageMediaPhoto',
- 'thumb' => read('tests/faust.preview.jpg'),
- 'thumb_w' => 90,
- 'thumb_h' => 90,
- 'caption' => 'This file was uploaded using MadelineProto',
- 'size' => filesize('tests/faust.jpg'),
- 'w' => 1280,
- 'h' => 914,
- ],
- ],
- ];
+ $this->sendPhoto(
+ peer: $update->chatId,
+ file: new LocalFile('tests/faust.jpg'),
+ caption: 'This file was uploaded using MadelineProto',
+ );
// GIF, secret chat
- $secret_media['gif'] = [
- 'peer' => $update->chatId,
- 'file' => 'tests/pony.mp4',
- 'message' => [
- '_' => 'decryptedMessage',
- 'ttl' => 0,
- 'message' => '',
- 'media' => [
- '_' => 'decryptedMessageMediaDocument',
- 'thumb' => read('tests/pony.preview.jpg'),
- 'thumb_w' => 90,
- 'thumb_h' => 90,
- 'mime_type' => mime_content_type('tests/pony.mp4'),
- 'caption' => 'test',
- 'file_name' => 'pony.mp4',
- 'size' => filesize('tests/faust.jpg'),
- 'attributes' => [
- ['_' => 'documentAttributeAnimated'],
- ],
- ],
- ],
- ];
+ $this->sendGif(
+ peer: $update->chatId,
+ file: new LocalFile('tests/pony.mp4'),
+ caption: 'This file was uploaded using MadelineProto',
+ );
// Sticker, secret chat
- $secret_media['sticker'] = [
- 'peer' => $update->chatId,
- 'file' => 'tests/lel.webp',
- 'message' => [
- '_' => 'decryptedMessage',
- 'ttl' => 0,
- 'message' => '',
- 'media' => [
- '_' => 'decryptedMessageMediaDocument',
- 'thumb' => read('tests/lel.preview.jpg'),
- 'thumb_w' => 90,
- 'thumb_h' => 90,
- 'mime_type' => mime_content_type('tests/lel.webp'),
- 'caption' => 'test',
- 'file_name' => 'lel.webp',
- 'size' => filesize('tests/lel.webp'),
- 'attributes' => [
- [
- '_' => 'documentAttributeImageSize',
- 'w' => 512,
- 'h' => 510,
- ],
- [
- '_' => 'documentAttributeSticker',
- 'alt' => 'LEL',
- 'stickerset' => ['_' => 'inputStickerSetEmpty'],
- ],
- ],
- ],
- ],
- ];
+ $this->sendSticker(
+ peer: $update->chatId,
+ file: new LocalFile('tests/lel.webp'),
+ mimeType: "image/webp"
+ );
- // Document, secrey chat
- $secret_media['document'] = [
- 'peer' => $update->chatId,
- 'file' => 'tests/60',
- 'message' => [
- '_' => 'decryptedMessage',
- 'ttl' => 0,
- 'message' => '',
- 'media' => [
- '_' => 'decryptedMessageMediaDocument',
- 'thumb' => read('tests/faust.preview.jpg'),
- 'thumb_w' => 90,
- 'thumb_h' => 90,
- 'mime_type' => 'magic/magic',
- 'caption' => 'test',
- 'file_name' => 'magic.magic',
- 'size' => filesize('tests/60'),
- 'attributes' => [
- [
- '_' => 'documentAttributeFilename',
- 'file_name' => 'fairy',
- ],
- ],
- ],
- ],
- ];
+ // Document, secret chat
+ $this->sendDocument(
+ peer: $update->chatId,
+ file: new LocalFile('tests/60'),
+ fileName: 'fairy'
+ );
// Video, secret chat
- $secret_media['video'] = [
- 'peer' => $update->chatId,
- 'file' => 'tests/swing.mp4',
- 'message' => [
- '_' => 'decryptedMessage',
- 'ttl' => 0,
- 'message' => '',
- 'media' => [
- '_' => 'decryptedMessageMediaDocument',
- 'thumb' => read('tests/swing.preview.jpg'),
- 'thumb_w' => 90,
- 'thumb_h' => 90,
- 'mime_type' => mime_content_type('tests/swing.mp4'),
- 'caption' => 'test',
- 'file_name' => 'swing.mp4',
- 'size' => filesize('tests/swing.mp4'),
- 'attributes' => [
- [
- '_' => 'documentAttributeVideo',
- 'duration' => 5,
- 'w' => 1280,
- 'h' => 720,
- ],
- ],
- ],
- ],
- ];
+ $this->sendVideo(
+ peer: $update->chatId,
+ file: new LocalFile('tests/swing.mp4'),
+ );
// audio, secret chat
- $secret_media['audio'] = [
- 'peer' => $update->chatId,
- 'file' => 'tests/mosconi.mp3',
- 'message' => [
- '_' => 'decryptedMessage',
- 'ttl' => 0,
- 'message' => '',
- 'media' => [
- '_' => 'decryptedMessageMediaDocument',
- 'thumb' => read('tests/faust.preview.jpg'),
- 'thumb_w' => 90,
- 'thumb_h' => 90,
- 'mime_type' => mime_content_type('tests/mosconi.mp3'),
- 'caption' => 'test',
- 'file_name' => 'mosconi.mp3',
- 'size' => filesize('tests/mosconi.mp3'),
- 'attributes' => [
- [
- '_' => 'documentAttributeAudio',
- 'voice' => false,
- 'duration' => 1,
- 'title' => 'AH NON LO SO IO',
- 'performer' => 'IL DIO GERMANO MOSCONI',
- ],
- ],
- ],
- ],
- ];
+ $this->sendAudio(
+ peer: $update->chatId,
+ file: new LocalFile('tests/mosconi.mp3'),
+ );
- $secret_media['voice'] = [
- 'peer' => $update->chatId,
- 'file' => 'tests/mosconi.mp3',
- 'message' => [
- '_' => 'decryptedMessage',
- 'ttl' => 0,
- 'message' => '',
- 'media' => [
- '_' => 'decryptedMessageMediaDocument',
- 'thumb' => read('tests/faust.preview.jpg'),
- 'thumb_w' => 90,
- 'thumb_h' => 90,
- 'mime_type' => mime_content_type('tests/mosconi.mp3'),
- 'caption' => 'test',
- 'file_name' => 'mosconi.mp3',
- 'size' => filesize('tests/mosconi.mp3'),
- 'attributes' => [
- [
- '_' => 'documentAttributeAudio',
- 'voice' => true,
- 'duration' => 1,
- 'title' => 'AH NON LO SO IO',
- 'performer' => 'IL DIO GERMANO MOSCONI',
- ],
- ],
- ],
- ],
- ];
-
- $promises = [];
- foreach ($secret_media as $type => $smessage) {
- $promises []= async($this->messages->sendEncryptedFile(...), $smessage);
- }
- await($promises);
+ $this->sendVoice(
+ peer: $update->chatId,
+ file: new LocalFile('tests/mosconi.mp3'),
+ );
$i = 0;
while ($i < 10) {
diff --git a/psalm-baseline.xml b/psalm-baseline.xml
index 35bbb516f..800d1cdd2 100644
--- a/psalm-baseline.xml
+++ b/psalm-baseline.xml
@@ -4115,6 +4115,8 @@
+
+
@@ -5483,7 +5485,7 @@
-
+
diff --git a/src/EventHandler/Media/AbstractVideo.php b/src/EventHandler/Media/AbstractVideo.php
index 3b5ad3495..d55bdae6a 100644
--- a/src/EventHandler/Media/AbstractVideo.php
+++ b/src/EventHandler/Media/AbstractVideo.php
@@ -44,9 +44,9 @@ abstract class AbstractVideo extends Media
bool $protected,
) {
parent::__construct($API, $rawMedia, $protected);
- $this->duration = $attribute['duration'];
+ $this->duration = $attribute['duration'] ?? 0.0;
$this->supportsStreaming = $attribute['supports_streaming'] ?? false;
- $this->width = $attribute['w'];
- $this->height = $attribute['h'];
+ $this->width = $attribute['w'] ?? 0;
+ $this->height = $attribute['h'] ?? 0;
}
}
diff --git a/src/InternalDoc.php b/src/InternalDoc.php
index 127bbe501..174b8bdef 100644
--- a/src/InternalDoc.php
+++ b/src/InternalDoc.php
@@ -1836,9 +1836,11 @@ abstract class InternalDoc
* @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, string $emoji = '', ?callable $callback = null, ?string $fileName = null, ?string $mimeType = null, ?int $ttl = 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
+ 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, string $mimeType, string $emoji = '', array $stickerSet = [
+ '_' => 'inputStickerSetEmpty',
+ ], ?callable $callback = null, ?string $fileName = null, ?int $ttl = 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, $emoji, $callback, $fileName, $mimeType, $ttl, $replyToMsgId, $topMsgId, $replyMarkup, $sendAs, $scheduleDate, $silent, $noForwards, $background, $clearDraft, $updateStickersetsOrder, $forceResend, $cancellation);
+ return $this->wrapper->getAPI()->sendSticker($peer, $file, $mimeType, $emoji, $stickerSet, $callback, $fileName, $ttl, $replyToMsgId, $topMsgId, $replyMarkup, $sendAs, $scheduleDate, $silent, $noForwards, $background, $clearDraft, $updateStickersetsOrder, $forceResend, $cancellation);
}
/**
* Sends a video.
diff --git a/src/MTProtoTools/FilesAbstraction.php b/src/MTProtoTools/FilesAbstraction.php
index b739b1461..5b5cd29f2 100644
--- a/src/MTProtoTools/FilesAbstraction.php
+++ b/src/MTProtoTools/FilesAbstraction.php
@@ -237,10 +237,11 @@ trait FilesAbstraction
public function sendSticker(
int|string $peer,
Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream $file,
+ string $mimeType,
string $emoji = '',
+ array $stickerSet = ['_' => 'inputStickerSetEmpty'],
?callable $callback = null,
?string $fileName = null,
- ?string $mimeType = null,
?int $ttl = null,
?int $replyToMsgId = null,
?int $topMsgId = null,
@@ -259,10 +260,10 @@ trait FilesAbstraction
type: Sticker::class,
mimeType: $mimeType,
thumb: null,
- attributesOrig: [],
+ attributesOrig: ['_' => 'documentAttributeSticker', 'alt' => $emoji, 'stickerset' => $stickerSet],
peer: $peer,
file: $file,
- caption: $emoji,
+ caption: '',
parseMode: ParseMode::TEXT,
callback: $callback,
fileName: $fileName,
@@ -760,6 +761,7 @@ trait FilesAbstraction
: $attributesOrig['waveform'],
],
],
+ Sticker::class => [$attributesOrig],
default => [],
};
if ($type === Gif::class) {
@@ -791,7 +793,7 @@ trait FilesAbstraction
$width = 0;
$height = 0;
- if ($type === Photo::class) {
+ if ($type === Photo::class || $type === Sticker::class) {
if (!\extension_loaded('gd')) {
throw Exception::extension('gd');
}
@@ -799,34 +801,36 @@ trait FilesAbstraction
$img = imagecreatefromstring($file);
$width = imagesx($img);
$height = imagesy($img);
- if ($width > $height) {
- $thumb_width = 90;
- $thumb_height = (int) (90*$height/$width);
- } elseif ($width < $height) {
- $thumb_width = (int) (90*$width/$height);
- $thumb_height = 90;
- } else {
- $thumb_width = 90;
- $thumb_height = 90;
- }
- Assert::lessThanEq($thumb_height, 90);
- Assert::lessThanEq($thumb_width, 90);
- $thumb = imagecreatetruecolor($thumb_width, $thumb_height);
- imagecopyresized($thumb, $img, 0, 0, 0, 0, $thumb_width, $thumb_height, $width, $height);
-
- $stream = fopen('php://memory', 'r+');
- imagepng($thumb, $stream);
- rewind($stream);
- $thumb = stream_get_contents($stream);
- fclose($stream);
- unset($stream);
$file = new ReadableBuffer($file);
+ if ($type === Photo::class) {
+ if ($width > $height) {
+ $thumb_width = 90;
+ $thumb_height = (int) (90*$height/$width);
+ } elseif ($width < $height) {
+ $thumb_width = (int) (90*$width/$height);
+ $thumb_height = 90;
+ } else {
+ $thumb_width = 90;
+ $thumb_height = 90;
+ }
+ Assert::lessThanEq($thumb_height, 90);
+ Assert::lessThanEq($thumb_width, 90);
+ $thumb = imagecreatetruecolor($thumb_width, $thumb_height);
+ imagecopyresized($thumb, $img, 0, 0, 0, 0, $thumb_width, $thumb_height, $width, $height);
+
+ $stream = fopen('php://memory', 'r+');
+ imagepng($thumb, $stream);
+ rewind($stream);
+ $thumb = stream_get_contents($stream);
+ fclose($stream);
+ unset($stream);
+ }
} elseif ($type === Video::class || $type === Gif::class) {
$this->extractVideoInfo(true, $attributesOrig['thumbSeek'], $file, $fileName, $callback, $cancellation, $mimeType, $attributes, $thumb);
} elseif ($type === Audio::class || $type === Voice::class) {
$this->extractAudioInfo(true, $file, $fileName, $callback, $cancellation, $mimeType, $attributes, $thumb);
} elseif ($mimeType === null) {
- $mimeType = $this->extractMime($file, $fileName, $callback, $cancellation);
+ $mimeType = $this->extractMime(true, $file, $fileName, $callback, $cancellation);
}
if ($thumb !== null && $thumb_width === 0) {
@@ -904,7 +908,7 @@ trait FilesAbstraction
} elseif ($type === Audio::class || $type === Voice::class) {
$this->extractAudioInfo(false, $file, $fileName, $callback, $cancellation, $mimeType, $attributes, $thumb);
} elseif ($mimeType === null) {
- $mimeType = $this->extractMime($file, $fileName, $callback, $cancellation);
+ $mimeType = $this->extractMime(false, $file, $fileName, $callback, $cancellation);
}
$method = 'messages.sendMedia';
@@ -1002,14 +1006,11 @@ trait FilesAbstraction
return $res;
}
- /**
- * @return list{array, string}
- */
- private function extractMime(Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream $file, ?string $fileName, ?callable $callback, ?Cancellation $cancellation): array
+ private function extractMime(bool $secret, Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream &$file, ?string $fileName, ?callable $callback, ?Cancellation $cancellation): string
{
$file = $this->getStream($file, $cancellation);
$p = new Pipe(1024*1024);
- $fileFuture = async(fn () => $this->upload(new StreamDuplicator($file, $p->getSink()), $fileName ?? '', $callback, cancellation: $cancellation));
+ $fileFuture = async(fn () => $this->upload(new StreamDuplicator($file, $p->getSink()), $fileName ?? '', $callback, $secret, $cancellation));
$buff = '';
while (\strlen($buff) < 1024*1024 && null !== $chunk = $p->getSource()->read($cancellation)) {
@@ -1019,7 +1020,8 @@ trait FilesAbstraction
$p->getSource()->close();
unset($p);
- return [$fileFuture->await(), (new finfo())->buffer($buff, FILEINFO_MIME_TYPE)];
+ $file = $fileFuture->await();
+ return (new finfo())->buffer($buff, FILEINFO_MIME_TYPE);
}
private function extractAudioInfo(bool $secret, Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream &$file, ?string $fileName, ?callable $callback, ?Cancellation $cancellation, ?string &$mimeType, array &$attributes, mixed &$thumb): void
{
@@ -1029,7 +1031,7 @@ trait FilesAbstraction
}
$this->logger->logger('Install ffmpeg for audio info extraction!');
if ($mimeType === null) {
- [$file, $mimeType] = $this->extractMime($file, $fileName, $callback, $cancellation);
+ $mimeType = $this->extractMime($secret, $file, $fileName, $callback, $cancellation);
}
return;
}
@@ -1102,7 +1104,7 @@ trait FilesAbstraction
}
$this->logger->logger('Install ffmpeg for video info extraction!');
if ($mimeType === null) {
- [$file, $mimeType] = $this->extractMime($file, $fileName, $callback, $cancellation);
+ $mimeType = $this->extractMime($secret, $file, $fileName, $callback, $cancellation);
}
return;
}
@@ -1143,7 +1145,9 @@ trait FilesAbstraction
$fileFuture = async(fn () => $this->upload(new StreamDuplicator($file, ...$streams), $fileName ?? '', $callback, $secret, $cancellation));
[$stdout, $stderr] = await($f);
- $thumb ??= new ReadableBuffer($stdout);
+ if ($stdout !== '') {
+ $thumb ??= new ReadableBuffer($stdout);
+ }
$process->join($cancellation);
if (preg_match('~Duration: (\d{2}:\d{2}:\d{2}\.\d{2}),.*? (\d{3,4})x(\d{3,4})~s', $stderr, $matches)) {
diff --git a/src/SecretChats/AuthKeyHandler.php b/src/SecretChats/AuthKeyHandler.php
index 39d63678a..8f062a82e 100644
--- a/src/SecretChats/AuthKeyHandler.php
+++ b/src/SecretChats/AuthKeyHandler.php
@@ -67,7 +67,8 @@ trait AuthKeyHandler
if ($user['type'] !== 'user') {
throw new AssertionError("Can only create a secret chat with a user!");
}
- $this->logger->logger('Creating secret chat with '.$user['user_id'].'...', Logger::VERBOSE);
+ $user = $user['user_id'];
+ $this->logger->logger('Creating secret chat with '.$user.'...', Logger::VERBOSE);
$dh_config = ($this->getDhConfig());
$this->logger->logger('Generating a...', Logger::VERBOSE);
$a = new BigInteger(Tools::random(256), 256);
diff --git a/tests/faust.preview.jpg b/tests/faust.preview.jpg
deleted file mode 100644
index 750e5ef96..000000000
Binary files a/tests/faust.preview.jpg and /dev/null differ