diff --git a/README.md b/README.md
index ac5e09e19..ec0335ad0 100644
--- a/README.md
+++ b/README.md
@@ -248,7 +248,7 @@ Want to add your own open-source project to this list? [Click here!](https://doc
* [FAQ](https://docs.madelineproto.xyz/docs/FAQ.html) - Here's a list of common MadelineProto questions and answers.
* [Upgrading from MadelineProto v7 to v8](https://docs.madelineproto.xyz/docs/UPGRADING.html) - MadelineProto v8 is a major MadelineProto update, that removes a large number of long-deprecated APIs: I've created this upgrade checklist, to simplify the upgrade process.
* [Using methods](https://docs.madelineproto.xyz/docs/USING_METHODS.html) - There are simplifications for many, if not all of, these methods.
- * [Named arguments (PHP 8+)](https://docs.madelineproto.xyz/docs/USING_METHODS.html#named-arguments)
+ * [Named arguments](https://docs.madelineproto.xyz/docs/USING_METHODS.html#named-arguments)
* [Peers](https://docs.madelineproto.xyz/docs/USING_METHODS.html#peers)
* [Files](https://docs.madelineproto.xyz/docs/FILES.html)
* [Secret chats](https://docs.madelineproto.xyz/docs/USING_METHODS.html#secret-chats)
diff --git a/docs b/docs
index 61cdff1c4..dbfd462ed 160000
--- a/docs
+++ b/docs
@@ -1 +1 @@
-Subproject commit 61cdff1c445fd53456b63d33a6feac12e4023514
+Subproject commit dbfd462edb6a96c6bb1214b5def5e350a669100e
diff --git a/examples/libtgvoipbot.php b/examples/libtgvoipbot.php
index ece6daa83..90e177778 100644
--- a/examples/libtgvoipbot.php
+++ b/examples/libtgvoipbot.php
@@ -47,7 +47,7 @@ class MyEventHandler extends SimpleEventHandler
public function convertCmd((Incoming&Message&HasAudio)|(Incoming&Message&HasDocument) $message): void
{
$reply = $message->reply("Conversion in progress...");
- try {
+ async(function () use ($message, $reply): void {
$pipe = self::getStreamPipe();
$sink = $pipe->getSink();
async(
@@ -62,9 +62,7 @@ class MyEventHandler extends SimpleEventHandler
fileName: $message->media->fileName.".ogg",
replyToMsgId: $message->id
);
- } finally {
- $reply->delete();
- }
+ })->finally($reply->delete(...));
}
}
diff --git a/psalm-baseline.xml b/psalm-baseline.xml
index fdd9746c0..b94bfebaf 100644
--- a/psalm-baseline.xml
+++ b/psalm-baseline.xml
@@ -908,6 +908,10 @@
copy
unwrap
+
+ $payload[0]
+ $payload[0]
+
$data
Wrapper
@@ -1508,6 +1512,9 @@
seek
seek
+
+ $l
+
diff --git a/src/API.php b/src/API.php
index 70f8c4e25..609b7b43e 100644
--- a/src/API.php
+++ b/src/API.php
@@ -51,7 +51,7 @@ final class API extends AbstractAPI
*
* @var string
*/
- public const RELEASE = '8.0.0-beta145';
+ public const RELEASE = '8.0.0-beta147';
/**
* Secret chat was not found.
*
diff --git a/src/Broadcast/InternalState.php b/src/Broadcast/InternalState.php
index cc3defb85..9b6eb29ce 100644
--- a/src/Broadcast/InternalState.php
+++ b/src/Broadcast/InternalState.php
@@ -24,6 +24,7 @@ use Amp\CancelledException;
use Amp\DeferredCancellation;
use danog\MadelineProto\Logger;
use danog\MadelineProto\MTProto;
+use Revolt\EventLoop;
use Throwable;
use Webmozart\Assert\Assert;
@@ -109,7 +110,7 @@ final class InternalState
}
private function notifyProgress(): void
{
- $this->API->saveUpdate(['_' => 'updateBroadcastProgress', 'progress' => $this->getProgress()]);
+ EventLoop::queue($this->API->saveUpdate(...), ['_' => 'updateBroadcastProgress', 'progress' => $this->getProgress()]);
}
private function gatherPeers(): void
{
diff --git a/src/Connection.php b/src/Connection.php
index 460407514..4a6489d6d 100644
--- a/src/Connection.php
+++ b/src/Connection.php
@@ -21,6 +21,8 @@ declare(strict_types=1);
namespace danog\MadelineProto;
use Amp\ByteStream\ClosedException;
+use Amp\ByteStream\ReadableBuffer;
+use Amp\ByteStream\ReadableStream;
use Amp\DeferredFuture;
use Amp\Sync\LocalMutex;
use AssertionError;
@@ -35,10 +37,12 @@ use danog\MadelineProto\MTProtoSession\Session;
use danog\MadelineProto\Stream\BufferedStreamInterface;
use danog\MadelineProto\Stream\ConnectionContext;
use danog\MadelineProto\Stream\MTProtoBufferInterface;
-use danog\MadelineProto\TL\Conversion\Extension;
use danog\MadelineProto\TL\Exception as TLException;
+use Revolt\EventLoop;
use Webmozart\Assert\Assert;
+use function Amp\ByteStream\buffer;
+
/**
* Connection class.
*
@@ -105,6 +109,10 @@ final class Connection
* Whether we're currently reading an MTProto packet.
*/
private bool $reading = false;
+ /**
+ * Whether we're currently writing an MTProto packet.
+ */
+ private bool $writing = false;
/**
* Logger instance.
*
@@ -160,6 +168,7 @@ final class Connection
*/
public function writing(bool $writing): void
{
+ $this->writing = $writing;
$this->shared->writing($writing, $this->id);
}
/**
@@ -177,6 +186,13 @@ final class Connection
{
return $this->reading;
}
+ /**
+ * Whether we're currently writing an MTProto packet.
+ */
+ public function isWriting(): bool
+ {
+ return $this->writing;
+ }
/**
* Indicate a received HTTP response.
*/
@@ -310,7 +326,7 @@ final class Connection
}
throw new AssertionError("Could not connect to DC {$this->datacenterId}!");
} finally {
- $lock->release();
+ EventLoop::queue($lock->release(...));
}
}
/**
@@ -362,16 +378,24 @@ final class Connection
}
}
if (\is_array($arguments['media']) && isset($arguments['media']['_'])) {
- if ($arguments['media']['_'] === 'inputMediaPhotoExternal') {
- $arguments['media']['_'] = 'inputMediaUploadedPhoto';
- $arguments['media']['file'] = new RemoteUrl($arguments['media']['url']);
- } elseif ($arguments['media']['_'] === 'inputMediaDocumentExternal') {
- $arguments['media']['_'] = 'inputMediaUploadedDocument';
- $arguments['media']['file'] = new RemoteUrl($arguments['media']['url']);
- $arguments['media']['mime_type'] = Extension::getMimeFromExtension(
- \pathinfo($arguments['media']['url'], PATHINFO_EXTENSION),
- 'application/octet-stream'
- );
+ $this->API->processMedia($arguments['media']);
+ if ($arguments['media']['_'] === 'inputMediaUploadedPhoto'
+ && (
+ $arguments['media']['file'] instanceof ReadableStream
+ || (
+ $arguments['media']['file'] instanceof FileCallback
+ && $arguments['media']['file']->file instanceof ReadableStream
+ )
+ )
+ ) {
+ if ($arguments['media']['file'] instanceof FileCallback) {
+ $arguments['media']['file'] = new FileCallback(
+ new ReadableBuffer(buffer($arguments['media']['file']->file)),
+ $arguments['media']['file']->callback
+ );
+ } else {
+ $arguments['media']['file'] = new ReadableBuffer(buffer($arguments['media']['file']));
+ }
}
}
} elseif ($method === 'messages.sendMultiMedia') {
diff --git a/src/DataCenter.php b/src/DataCenter.php
index cf4b01a68..27f6a7278 100644
--- a/src/DataCenter.php
+++ b/src/DataCenter.php
@@ -41,6 +41,7 @@ use danog\MadelineProto\Stream\MTProtoTransport\ObfuscatedStream;
use danog\MadelineProto\Stream\Transport\DefaultStream;
use danog\MadelineProto\Stream\Transport\WssStream;
use danog\MadelineProto\Stream\Transport\WsStream;
+use Revolt\EventLoop;
/**
* @psalm-type TDcOption=array{
@@ -387,7 +388,7 @@ final class DataCenter
$this->sockets[$dc]->setExtra($this->API, $dc, $ctxs);
$this->sockets[$dc]->connect();
} finally {
- $lock->release();
+ EventLoop::queue($lock->release(...));
}
}
return $this->sockets[$dc];
diff --git a/src/DataCenterConnection.php b/src/DataCenterConnection.php
index f52b7c8b3..d666a08df 100644
--- a/src/DataCenterConnection.php
+++ b/src/DataCenterConnection.php
@@ -197,7 +197,7 @@ final class DataCenterConnection implements JsonSerializable
$this->syncAuthorization();
}
} finally {
- $lock->release();
+ EventLoop::queue($lock->release(...));
}
if ($this->hasTempAuthKey()) {
$connection->pingHttpWaiter();
diff --git a/src/Db/CacheContainer.php b/src/Db/CacheContainer.php
index a03404db0..945ddf8b3 100644
--- a/src/Db/CacheContainer.php
+++ b/src/Db/CacheContainer.php
@@ -157,7 +157,7 @@ final class CacheContainer
$this->ttl = $newTtl;
$this->cache = $newValues;
} finally {
- $lock->release();
+ EventLoop::queue($lock->release(...));
}
}
}
diff --git a/src/Db/Driver/Mysql.php b/src/Db/Driver/Mysql.php
index 65ae581fa..a8b5e891f 100644
--- a/src/Db/Driver/Mysql.php
+++ b/src/Db/Driver/Mysql.php
@@ -24,6 +24,7 @@ use danog\MadelineProto\Logger;
use danog\MadelineProto\Settings\Database\Mysql as DatabaseMysql;
use PDO;
use PDOException;
+use Revolt\EventLoop;
use Throwable;
/**
@@ -79,7 +80,7 @@ final class Mysql
];
}
} finally {
- $lock->release();
+ EventLoop::queue($lock->release(...));
}
return self::$connections[$dbKey];
diff --git a/src/Db/Driver/Postgres.php b/src/Db/Driver/Postgres.php
index 362312d90..082c2e0a1 100644
--- a/src/Db/Driver/Postgres.php
+++ b/src/Db/Driver/Postgres.php
@@ -21,6 +21,7 @@ use Amp\Postgres\PostgresConnectionPool;
use Amp\Sync\LocalKeyedMutex;
use danog\MadelineProto\Logger;
use danog\MadelineProto\Settings\Database\Postgres as DatabasePostgres;
+use Revolt\EventLoop;
use Throwable;
/**
@@ -51,7 +52,7 @@ final class Postgres
self::$connections[$dbKey] = new PostgresConnectionPool($config, $settings->getMaxConnections(), $settings->getIdleTimeout());
}
} finally {
- $lock->release();
+ EventLoop::queue($lock->release(...));
}
return self::$connections[$dbKey];
diff --git a/src/Db/Driver/Redis.php b/src/Db/Driver/Redis.php
index 95574e364..28360a2a1 100644
--- a/src/Db/Driver/Redis.php
+++ b/src/Db/Driver/Redis.php
@@ -21,6 +21,7 @@ use Amp\Redis\RedisClient;
use Amp\Redis\RedisConfig;
use Amp\Sync\LocalKeyedMutex;
use danog\MadelineProto\Settings\Database\Redis as DatabaseRedis;
+use Revolt\EventLoop;
use function Amp\Redis\createRedisConnector;
@@ -51,7 +52,7 @@ final class Redis
self::$connections[$dbKey]->ping();
}
} finally {
- $lock->release();
+ EventLoop::queue($lock->release(...));
}
return self::$connections[$dbKey];
diff --git a/src/EventHandler.php b/src/EventHandler.php
index 991999857..89664928d 100644
--- a/src/EventHandler.php
+++ b/src/EventHandler.php
@@ -263,7 +263,7 @@ abstract class EventHandler extends AbstractAPI
} finally {
$this->startDeferred = null;
$startDeferred->complete();
- $lock->release();
+ EventLoop::queue($lock->release(...));
}
}
/**
diff --git a/src/EventHandler/Action.php b/src/EventHandler/Action.php
index 003d38a1f..8883ebfaa 100644
--- a/src/EventHandler/Action.php
+++ b/src/EventHandler/Action.php
@@ -16,6 +16,7 @@
namespace danog\MadelineProto\EventHandler;
+use danog\MadelineProto\EventHandler\Action\Cancel;
use danog\MadelineProto\EventHandler\Action\ChooseContact;
use danog\MadelineProto\EventHandler\Action\ChooseSticker;
use danog\MadelineProto\EventHandler\Action\EmojiSeen;
@@ -56,7 +57,8 @@ abstract class Action implements JsonSerializable
}
return match ($type) {
'sendMessageTypingAction' => new Typing,
- 'sendMessageCancelAction' => new GamePlay,
+ 'sendMessageCancelAction' => new Cancel,
+ 'sendMessageGamePlayAction' => new GamePlay,
'sendMessageGeoLocationAction' => new GeoLocation,
'sendMessageChooseContactAction' => new ChooseContact,
'sendMessageChooseStickerAction' => new ChooseSticker,
@@ -79,7 +81,8 @@ abstract class Action implements JsonSerializable
{
return match (true) {
$this instanceof Typing => [ '_' => 'sendMessageTypingAction' ],
- $this instanceof GamePlay => [ '_' => 'sendMessageCancelAction' ],
+ $this instanceof Cancel => [ '_' => 'sendMessageCancelAction' ],
+ $this instanceof GamePlay => [ '_' => 'sendMessageGamePlayAction' ],
$this instanceof GeoLocation => [ '_' => 'sendMessageGeoLocationAction' ],
$this instanceof ChooseContact => [ '_' => 'sendMessageChooseContactAction' ],
$this instanceof ChooseSticker => [ '_' => 'sendMessageChooseStickerAction' ],
diff --git a/src/Ipc/Client.php b/src/Ipc/Client.php
index c21d11e00..dfa9678fd 100644
--- a/src/Ipc/Client.php
+++ b/src/Ipc/Client.php
@@ -237,26 +237,19 @@ final class Client extends ClientAbstract
*/
public function methodCallAsyncRead(string $method, array $args = [], array $aargs = [])
{
- if (($method === 'messages.editInlineBotMessage' ||
+ if ((
+ $method === 'messages.editInlineBotMessage' ||
$method === 'messages.uploadMedia' ||
$method === 'messages.sendMedia' ||
- $method === 'messages.editMessage') &&
- isset($args['media']['file']) &&
- $args['media']['file'] instanceof FileCallbackInterface
- ) {
- $params = [$method, &$args, $aargs];
- $wrapper = Wrapper::create($params, $this->session, $this->logger);
- $wrapper->wrap($args['media']['file'], true);
- return $this->__call('methodCallAsyncRead', $wrapper);
+ $method === 'messages.editMessage'
+ ) && isset($args['media']) && \is_array($args['media'])) {
+ $this->processMedia($args['media'], true);
} elseif ($method === 'messages.sendMultiMedia' && isset($args['multi_media'])) {
- $params = [$method, &$args, $aargs];
- $wrapper = Wrapper::create($params, $this->session, $this->logger);
foreach ($args['multi_media'] as &$media) {
- if (isset($media['media']['file']) && $media['media']['file'] instanceof FileCallbackInterface) {
- $wrapper->wrap($media['media']['file'], true);
+ if (\is_array($media['media'])) {
+ $this->processMedia($media['media'], true);
}
}
- return $this->__call('methodCallAsyncRead', $wrapper);
}
return $this->__call('methodCallAsyncRead', [$method, $args, $aargs]);
}
diff --git a/src/Ipc/Server.php b/src/Ipc/Server.php
index 694de6be9..894ba9c66 100644
--- a/src/Ipc/Server.php
+++ b/src/Ipc/Server.php
@@ -214,14 +214,17 @@ class Server extends Loop
} catch (Throwable $e) {
Logger::log("Exception in IPC connection: $e");
} finally {
- try {
- $socket->disconnect();
- } catch (Throwable $e) {
- }
- if ($payload === self::SHUTDOWN) {
- Shutdown::removeCallback('restarter');
- $this->stop();
- }
+ EventLoop::queue(function () use ($socket, $payload): void {
+ try {
+ $socket->disconnect();
+ } catch (Throwable $e) {
+ Logger::log("Exception during shutdown in IPC connection: $e");
+ }
+ if ($payload === self::SHUTDOWN) {
+ Shutdown::removeCallback('restarter');
+ $this->stop();
+ }
+ });
}
}
/**
@@ -243,10 +246,13 @@ class Server extends Loop
$result = new ExitFailure($e);
} finally {
if (isset($wrapper)) {
- try {
- $wrapper->disconnect();
- } catch (Throwable $e) {
- }
+ EventLoop::queue(function () use ($wrapper): void {
+ try {
+ $wrapper->disconnect();
+ } catch (Throwable $e) {
+ Logger::log("Exception during shutdown in IPC connection: $e");
+ }
+ });
}
}
try {
diff --git a/src/Ipc/Wrapper.php b/src/Ipc/Wrapper.php
index 8a3591882..d990a2a7c 100644
--- a/src/Ipc/Wrapper.php
+++ b/src/Ipc/Wrapper.php
@@ -20,6 +20,7 @@ use Amp\ByteStream\ReadableStream as ByteStreamReadableStream;
use Amp\ByteStream\WritableStream as ByteStreamWritableStream;
use Amp\Cancellation;
use Amp\Ipc\Sync\ChannelledSocket;
+use danog\MadelineProto\FileCallback as MadelineProtoFileCallback;
use danog\MadelineProto\FileCallbackInterface;
use danog\MadelineProto\Ipc\Wrapper\Cancellation as WrapperCancellation;
use danog\MadelineProto\Ipc\Wrapper\FileCallback;
@@ -107,6 +108,13 @@ final class Wrapper extends ClientAbstract
public function wrap(mixed &$callback, bool $wrapObjects = true): void
{
if (\is_object($callback) && $wrapObjects) {
+ if ($callback instanceof FileCallbackInterface) {
+ $file = $callback->getFile();
+ if ($file instanceof ByteStreamReadableStream) {
+ $this->wrap($file, true);
+ $callback = new MadelineProtoFileCallback($file, $callback);
+ }
+ }
$ids = [];
foreach (\get_class_methods($callback) as $method) {
$id = $this->id++;
@@ -115,9 +123,9 @@ final class Wrapper extends ClientAbstract
}
$class = null;
if ($callback instanceof ByteStreamReadableStream) {
- $class = \method_exists($callback, 'seek') ? ReadableStream::class : SeekableReadableStream::class;
+ $class = \method_exists($callback, 'seek') ? SeekableReadableStream::class : ReadableStream::class;
} elseif ($callback instanceof ByteStreamWritableStream) {
- $class = \method_exists($callback, 'seek') ? WritableStream::class : SeekableWritableStream::class;
+ $class = \method_exists($callback, 'seek') ? SeekableWritableStream::class : WritableStream::class;
} elseif ($callback instanceof FileCallbackInterface) {
$class = FileCallback::class;
} elseif ($callback instanceof Cancellation) {
@@ -154,7 +162,7 @@ final class Wrapper extends ClientAbstract
EventLoop::queue($this->clientRequest(...), $id++, $payload);
}
} finally {
- $this->server->disconnect();
+ EventLoop::queue($this->server->disconnect(...));
}
}
@@ -175,11 +183,11 @@ final class Wrapper extends ClientAbstract
try {
$this->server->send([$id, $result]);
} catch (Throwable $e) {
- $this->logger->logger("Got error while trying to send result of reverse method: $e", Logger::ERROR);
+ $this->logger->logger("Got error while trying to send result of reverse method {$payload[0]}: $e", Logger::ERROR);
try {
$this->server->send([$id, new ExitFailure($e)]);
} catch (Throwable $e) {
- $this->logger->logger("Got error while trying to send error of error of reverse method: $e", Logger::ERROR);
+ $this->logger->logger("Got error while trying to send error of error of reverse method {$payload[0]}: $e", Logger::ERROR);
}
}
}
diff --git a/src/Ipc/Wrapper/ClosableTrait.php b/src/Ipc/Wrapper/ClosableTrait.php
index 70368b69b..90ab82a29 100644
--- a/src/Ipc/Wrapper/ClosableTrait.php
+++ b/src/Ipc/Wrapper/ClosableTrait.php
@@ -62,6 +62,6 @@ trait ClosableTrait
final public function __destruct()
{
- $this->close();
+ EventLoop::queue($this->close(...));
}
}
diff --git a/src/Loop/Connection/CheckLoop.php b/src/Loop/Connection/CheckLoop.php
index b595b900a..5264299aa 100644
--- a/src/Loop/Connection/CheckLoop.php
+++ b/src/Loop/Connection/CheckLoop.php
@@ -132,9 +132,9 @@ final class CheckLoop extends Loop
}
}
$this->connection->flush();
- } catch (CancelledException) {
- $this->logger->logger("We did not receive a response for {$this->timeout} seconds: reconnecting and exiting check loop on DC {$this->datacenter}");
- EventLoop::queue($this->connection->reconnect(...));
+ //} catch (CancelledException) {
+ //$this->logger->logger("We did not receive a response for {$this->timeout} seconds: reconnecting and exiting check loop on DC {$this->datacenter}");
+ //EventLoop::queue($this->connection->reconnect(...));
} catch (\Throwable $e) {
$this->logger->logger("Got exception in check loop for DC {$this->datacenter}");
$this->logger->logger((string) $e);
diff --git a/src/Loop/Connection/ReadLoop.php b/src/Loop/Connection/ReadLoop.php
index 01faaa5b6..1e9856eb0 100644
--- a/src/Loop/Connection/ReadLoop.php
+++ b/src/Loop/Connection/ReadLoop.php
@@ -64,6 +64,7 @@ final class ReadLoop extends Loop
if ($e instanceof NothingInTheSocketException
&& !$this->connection->hasPendingCalls()
&& $this->connection->isMedia()
+ && !$this->connection->isWriting()
) {
$this->logger->logger("Got NothingInTheSocketException in DC {$this->datacenter}, disconnecting because we have nothing to do...", Logger::ERROR);
$this->connection->disconnect(true);
diff --git a/src/Loop/VoIP/DjLoop.php b/src/Loop/VoIP/DjLoop.php
index c565d2441..3c2c85362 100644
--- a/src/Loop/VoIP/DjLoop.php
+++ b/src/Loop/VoIP/DjLoop.php
@@ -211,7 +211,7 @@ final class DjLoop extends VoIPLoop
Ogg::convert($f, $pipe->getSink(), $cancellation);
} catch (CancelledException) {
} finally {
- $pipe->getSink()->close();
+ EventLoop::queue($pipe->getSink()->close(...));
}
});
$it = new Ogg($pipe->getSource());
diff --git a/src/MTProto.php b/src/MTProto.php
index 96ff1b2d8..43d897e5d 100644
--- a/src/MTProto.php
+++ b/src/MTProto.php
@@ -1670,7 +1670,7 @@ final class MTProto implements TLCallback, LoggerGetter
$this->logger->logger('Reported!');
}
} finally {
- $lock->release();
+ EventLoop::queue($lock->release(...));
}
}
/**
diff --git a/src/MTProtoSession/CallHandler.php b/src/MTProtoSession/CallHandler.php
index f900e6004..8592573c3 100644
--- a/src/MTProtoSession/CallHandler.php
+++ b/src/MTProtoSession/CallHandler.php
@@ -130,7 +130,7 @@ trait CallHandler
if (isset($args['message']) && \is_string($args['message']) && \mb_strlen($args['message'], 'UTF-8') > ($this->API->getConfig())['message_length_max'] && \mb_strlen($this->API->parseMode($args)['message'], 'UTF-8') > ($this->API->getConfig())['message_length_max']) {
$args = $this->API->splitToChunks($args);
$promises = [];
- $aargs['queue'] = $method;
+ $aargs['queue'] = $method.' '.\time();
$aargs['multiple'] = true;
}
if (isset($aargs['multiple'])) {
diff --git a/src/MTProtoSession/ResponseHandler.php b/src/MTProtoSession/ResponseHandler.php
index a63befca3..3b8f668c3 100644
--- a/src/MTProtoSession/ResponseHandler.php
+++ b/src/MTProtoSession/ResponseHandler.php
@@ -312,6 +312,17 @@ trait ResponseHandler
}
EventLoop::queue(closure: $this->methodRecall(...), message_id: $request->getMsgId(), datacenter: $datacenter);
return null;
+ case 400:
+ if ($request->hasQueue() &&
+ (
+ $response['error_message'] === 'MSG_WAIT_FAILED'
+ || $response['error_message'] === 'MSG_WAIT_TIMEOUT'
+ )
+ ) {
+ EventLoop::queue(closure: $this->methodRecall(...), message_id: $request->getMsgId());
+ return null;
+ }
+ return fn () => new RPCErrorException($response['error_message'], $response['error_code'], $request->getConstructor());
case 401:
switch ($response['error_message']) {
case 'USER_DEACTIVATED':
diff --git a/src/MTProtoTools/FileServer.php b/src/MTProtoTools/FileServer.php
index 7f6a38f33..a6229e36f 100644
--- a/src/MTProtoTools/FileServer.php
+++ b/src/MTProtoTools/FileServer.php
@@ -35,6 +35,7 @@ use danog\MadelineProto\Settings;
use danog\MadelineProto\Settings\AppInfo;
use danog\MadelineProto\Tools;
use Exception;
+use Revolt\EventLoop;
use Throwable;
use function Amp\File\exists;
@@ -216,7 +217,7 @@ trait FileServer
$this->checkDownloadScript($f);
return self::$checkedAutoload[$autoloadPath] = $f;
} finally {
- $lock->release();
+ EventLoop::queue($lock->release(...));
}
}
@@ -249,7 +250,7 @@ trait FileServer
self::$checkedScripts[$scriptUrl] = true;
} finally {
- $lock->release();
+ EventLoop::queue($lock->release(...));
}
}
}
diff --git a/src/MTProtoTools/Files.php b/src/MTProtoTools/Files.php
index ddf2122d5..fae3e7ffe 100644
--- a/src/MTProtoTools/Files.php
+++ b/src/MTProtoTools/Files.php
@@ -23,7 +23,21 @@ namespace danog\MadelineProto\MTProtoTools;
use Amp\DeferredFuture;
use Amp\Future;
use Amp\Http\Client\Request;
+use AssertionError;
use danog\MadelineProto\EventHandler\Media;
+use danog\MadelineProto\EventHandler\Media\AnimatedSticker;
+use danog\MadelineProto\EventHandler\Media\Audio;
+use danog\MadelineProto\EventHandler\Media\CustomEmoji;
+use danog\MadelineProto\EventHandler\Media\Document;
+use danog\MadelineProto\EventHandler\Media\DocumentPhoto;
+use danog\MadelineProto\EventHandler\Media\Gif;
+use danog\MadelineProto\EventHandler\Media\MaskSticker;
+use danog\MadelineProto\EventHandler\Media\Photo;
+use danog\MadelineProto\EventHandler\Media\RoundVideo;
+use danog\MadelineProto\EventHandler\Media\StaticSticker;
+use danog\MadelineProto\EventHandler\Media\Video;
+use danog\MadelineProto\EventHandler\Media\VideoSticker;
+use danog\MadelineProto\EventHandler\Media\Voice;
use danog\MadelineProto\EventHandler\Message;
use danog\MadelineProto\Exception;
use danog\MadelineProto\FileCallbackInterface;
@@ -57,8 +71,88 @@ use function Amp\Future\awaitFirst;
trait Files
{
use FilesLogic;
- use FilesAbstraction;
use FileServer;
+ /**
+ * Wrap a media constructor into an abstract Media object.
+ */
+ public function wrapMedia(array $media, bool $protected = false): ?Media
+ {
+ if ($media['_'] === 'messageMediaPhoto') {
+ if (!isset($media['photo'])) {
+ return null;
+ }
+ return new Photo($this, $media, $protected);
+ }
+ if ($media['_'] !== 'messageMediaDocument') {
+ return null;
+ }
+ if (!isset($media['document'])) {
+ return null;
+ }
+ $has_video = null;
+ $has_document_photo = null;
+ $has_animated = false;
+ foreach ($media['document']['attributes'] as $attr) {
+ $t = $attr['_'];
+ if ($t === 'documentAttributeImageSize') {
+ $has_document_photo = $attr;
+ continue;
+ }
+ if ($t === 'documentAttributeAnimated') {
+ $has_animated = true;
+ continue;
+ }
+ if ($t === 'documentAttributeSticker') {
+ if ($has_video) {
+ return new VideoSticker($this, $media, $attr, $has_video, $protected);
+ }
+
+ if ($has_document_photo === null) {
+ throw new AssertionError("has_document_photo === null: ".\json_encode($media['document']));
+ }
+
+ if ($attr['mask']) {
+ return new MaskSticker($this, $media, $attr, $has_document_photo, $protected);
+ }
+
+ if ($media['document']['mime_type'] === 'application/x-tgsticker') {
+ return new AnimatedSticker($this, $media, $attr, $has_document_photo, $protected);
+ }
+
+ return new StaticSticker($this, $media, $attr, $has_document_photo, $protected);
+ }
+ if ($t === 'documentAttributeVideo') {
+ $has_video = $attr;
+ continue;
+ }
+ if ($t === 'documentAttributeAudio') {
+ return $attr['voice']
+ ? new Voice($this, $media, $attr, $protected)
+ : new Audio($this, $media, $attr, $protected);
+ }
+ if ($t === 'documentAttributeCustomEmoji') {
+ if ($has_document_photo === null) {
+ throw new AssertionError("has_document_photo === null: ".\json_encode($media['document']));
+ }
+ return new CustomEmoji($this, $media, $attr, $has_document_photo, $protected);
+ }
+ }
+ if ($has_animated) {
+ if ($has_video === null) {
+ throw new AssertionError("has_video === null: ".\json_encode($media['document']));
+ }
+ return new Gif($this, $media, $has_video, $protected);
+ }
+ if ($has_video) {
+ return $has_video['round_message']
+ ? new RoundVideo($this, $media, $has_video, $protected)
+ : new Video($this, $media, $has_video, $protected);
+ }
+ if ($has_document_photo) {
+ return new DocumentPhoto($this, $media, $has_document_photo, $protected);
+ }
+ return new Document($this, $media, $protected);
+ }
/**
* Upload file from URL.
*
@@ -859,16 +953,14 @@ trait Files
$this->logger->logger('Waiting for lock of file to download...');
$unlock = Tools::flock("$file.lock", LOCK_EX);
$this->logger->logger('Got lock of file to download');
- try {
- $this->downloadToStream($messageMedia, $stream, $cb, $size, -1);
- } finally {
+ async($this->downloadToStream(...), $messageMedia, $stream, $cb, $size, -1)->finally(function () use ($stream, $unlock, $file): void {
+ $stream->close();
$unlock();
try {
deleteFile("$file.lock");
- } catch (Throwable) {
+ } catch (\Throwable) {
}
- $stream->close();
- }
+ })->await();
return $file;
}
/**
diff --git a/src/MTProtoTools/FilesAbstraction.php b/src/MTProtoTools/FilesAbstraction.php
index 14b129974..fae3ce0c4 100644
--- a/src/MTProtoTools/FilesAbstraction.php
+++ b/src/MTProtoTools/FilesAbstraction.php
@@ -24,19 +24,8 @@ use Amp\ByteStream\ReadableStream;
use AssertionError;
use danog\MadelineProto\BotApiFileId;
use danog\MadelineProto\EventHandler\Media;
-use danog\MadelineProto\EventHandler\Media\AnimatedSticker;
-use danog\MadelineProto\EventHandler\Media\Audio;
-use danog\MadelineProto\EventHandler\Media\CustomEmoji;
use danog\MadelineProto\EventHandler\Media\Document;
-use danog\MadelineProto\EventHandler\Media\DocumentPhoto;
-use danog\MadelineProto\EventHandler\Media\Gif;
-use danog\MadelineProto\EventHandler\Media\MaskSticker;
use danog\MadelineProto\EventHandler\Media\Photo;
-use danog\MadelineProto\EventHandler\Media\RoundVideo;
-use danog\MadelineProto\EventHandler\Media\StaticSticker;
-use danog\MadelineProto\EventHandler\Media\Video;
-use danog\MadelineProto\EventHandler\Media\VideoSticker;
-use danog\MadelineProto\EventHandler\Media\Voice;
use danog\MadelineProto\EventHandler\Message;
use danog\MadelineProto\FileCallback;
use danog\MadelineProto\LocalFile;
diff --git a/src/MTProtoTools/FilesLogic.php b/src/MTProtoTools/FilesLogic.php
index 9fe9beda0..cd56539a7 100644
--- a/src/MTProtoTools/FilesLogic.php
+++ b/src/MTProtoTools/FilesLogic.php
@@ -17,6 +17,7 @@
namespace danog\MadelineProto\MTProtoTools;
use Amp\ByteStream\Pipe;
+use Amp\ByteStream\ReadableBuffer;
use Amp\ByteStream\ReadableResourceStream;
use Amp\ByteStream\ReadableStream;
use Amp\ByteStream\StreamException;
@@ -34,6 +35,7 @@ use danog\MadelineProto\BotApiFileId;
use danog\MadelineProto\EventHandler\Media;
use danog\MadelineProto\EventHandler\Message;
use danog\MadelineProto\Exception;
+use danog\MadelineProto\FileCallback;
use danog\MadelineProto\FileCallbackInterface;
use danog\MadelineProto\Lang;
use danog\MadelineProto\LocalFile;
@@ -55,6 +57,7 @@ use Webmozart\Assert\Assert;
use const FILTER_VALIDATE_URL;
use function Amp\async;
+use function Amp\ByteStream\buffer;
use function Amp\File\exists;
use function Amp\File\getSize;
@@ -67,6 +70,7 @@ use function Amp\File\openFile;
*/
trait FilesLogic
{
+ use FilesAbstraction;
/**
* Download file to browser.
*
@@ -139,13 +143,7 @@ trait FilesLogic
{
$pipe = new Pipe(1024*1024);
$sink = $pipe->getSink();
- EventLoop::queue(function () use ($messageMedia, $sink, $cb, $offset, $end): void {
- try {
- $this->downloadToStream($messageMedia, $sink, $cb, $offset, $end);
- } finally {
- $sink->close();
- }
- });
+ async($this->downloadToStream(...), $messageMedia, $sink, $cb, $offset, $end)->finally($sink->close(...));
return $pipe->getSource();
}
/**
@@ -191,7 +189,7 @@ trait FilesLogic
}
$stream->write($payload);
} finally {
- $l->release();
+ EventLoop::queue($l->release(...));
}
return \strlen($payload);
};
@@ -260,6 +258,42 @@ trait FilesLogic
return $this->upload($file, $fileName, $cb, true);
}
+ /**
+ * @internal
+ */
+ public function processMedia(array &$media, bool $upload = false): void
+ {
+ if ($media['_'] === 'inputMediaPhotoExternal') {
+ $media['_'] = 'inputMediaUploadedPhoto';
+ if ($media['url'] instanceof FileCallbackInterface) {
+ $media['file'] = new FileCallback(
+ new RemoteUrl($media['url']->getFile()),
+ $media['url']
+ );
+ } else {
+ $media['file'] = new RemoteUrl($media['url']);
+ }
+ unset($media['url']);
+ } elseif ($media['_'] === 'inputMediaDocumentExternal') {
+ $media['_'] = 'inputMediaUploadedDocument';
+ if ($media['url'] instanceof FileCallbackInterface) {
+ $media['file'] = new FileCallback(
+ new RemoteUrl($url = $media['url']->getFile()),
+ $media['url']
+ );
+ } else {
+ $media['file'] = new RemoteUrl($url = $media['url']);
+ }
+ unset($media['url']);
+ $media['mime_type'] = Extension::getMimeFromExtension(
+ \pathinfo($url, PATHINFO_EXTENSION),
+ 'application/octet-stream'
+ );
+ }
+ if ($upload) {
+ $media['file'] = $this->upload($media['file']);
+ }
+ }
/**
* Upload file.
*
@@ -316,11 +350,7 @@ trait FilesLogic
}
$stream = openFile($file, 'rb');
$mime = Extension::getMimeFromFile($file);
- try {
- return $this->uploadFromStream($stream, $size, $mime, $fileName, $cb, $encrypted);
- } finally {
- $stream->close();
- }
+ return async($this->uploadFromStream(...), $stream, $size, $mime, $fileName, $cb, $encrypted)->finally($stream->close(...))->await();
}
/**
@@ -356,6 +386,17 @@ trait FilesLogic
}
}
$created = false;
+ if (!$size) {
+ if ($seekable && \method_exists($stream, 'tell')) {
+ $stream->seek(0, Whence::End);
+ $size = $stream->tell();
+ $stream->seek(0);
+ } elseif ($stream instanceof ReadableBuffer) {
+ $stream = buffer($stream);
+ $size = \strlen($stream);
+ $stream = new ReadableBuffer($stream);
+ }
+ }
if ($stream instanceof File) {
$lock = new LocalMutex;
$callable = static function (int $offset, int $size) use ($stream, $seekable, $lock) {
@@ -369,7 +410,7 @@ trait FilesLogic
}
return $stream->read(null, $size);
} finally {
- $l->release();
+ EventLoop::queue($l->release(...));
}
};
} else {
@@ -392,11 +433,6 @@ trait FilesLogic
};
$seekable = false;
}
- if (!$size && $seekable && \method_exists($stream, 'tell')) {
- $stream->seek(0, Whence::End);
- $size = $stream->tell();
- $stream->seek(0);
- }
$res = $this->uploadFromCallable($callable, $size, $mime, $fileName, $cb, $seekable, $encrypted);
if ($created) {
/** @var StreamInterface $stream */
diff --git a/src/MTProtoTools/MinDatabase.php b/src/MTProtoTools/MinDatabase.php
index 6d4e69e03..6b2e006ab 100644
--- a/src/MTProtoTools/MinDatabase.php
+++ b/src/MTProtoTools/MinDatabase.php
@@ -220,7 +220,7 @@ final class MinDatabase implements TLCallback
}
} finally {
unset($this->pendingDb[$id]);
- $lock->release();
+ EventLoop::queue($lock->release(...));
}
}
public function populateFrom(array $object)
diff --git a/src/MTProtoTools/PeerDatabase.php b/src/MTProtoTools/PeerDatabase.php
index 517e55673..be2d92114 100644
--- a/src/MTProtoTools/PeerDatabase.php
+++ b/src/MTProtoTools/PeerDatabase.php
@@ -289,7 +289,7 @@ final class PeerDatabase implements TLCallback
$this->usernames[$username] = $id;
}
} finally {
- $lock->release();
+ EventLoop::queue($lock->release(...));
}
}
@@ -450,7 +450,7 @@ final class PeerDatabase implements TLCallback
if (isset($o) && $this->pendingDb[$id] === $o) {
unset($this->pendingDb[$id]);
}
- $lock->release();
+ EventLoop::queue($lock->release(...));
}
}
public function addChatBlocking(int $chat): void
@@ -584,7 +584,7 @@ final class PeerDatabase implements TLCallback
if (isset($o) && $this->pendingDb[$id] === $o) {
unset($this->pendingDb[$id]);
}
- $lock->release();
+ EventLoop::queue($lock->release(...));
}
}
/**
diff --git a/src/MTProtoTools/ReferenceDatabase.php b/src/MTProtoTools/ReferenceDatabase.php
index cde960cb9..e1f32bf4f 100644
--- a/src/MTProtoTools/ReferenceDatabase.php
+++ b/src/MTProtoTools/ReferenceDatabase.php
@@ -148,7 +148,7 @@ final class ReferenceDatabase implements TLCallback
$this->db[$location] = $locationValue;
} finally {
unset($this->pendingDb[$location]);
- $lock->release();
+ EventLoop::queue($lock->release(...));
}
}
public function getMethodAfterResponseDeserializationCallbacks(): array
diff --git a/src/Magic.php b/src/Magic.php
index fa7d1a72f..badd6426c 100644
--- a/src/Magic.php
+++ b/src/Magic.php
@@ -308,6 +308,9 @@ final class Magic
throw Exception::extension($extension);
}
}
+ if (\extension_loaded('psr')) {
+ throw new Exception("Please uninstall the psr extension to use MadelineProto!");
+ }
self::$BIG_ENDIAN = \pack('L', 1) === \pack('N', 1);
self::$hasOpenssl = \extension_loaded('openssl');
self::$emojis = \json_decode(self::JSON_EMOJIS);
diff --git a/src/TL/TL.php b/src/TL/TL.php
index a29f941cc..1ef0c085a 100644
--- a/src/TL/TL.php
+++ b/src/TL/TL.php
@@ -981,12 +981,6 @@ final class TL implements TLInterface
case 'true':
$x[$arg['name']] = ($x[$arg['flag']] & $arg['pow']) !== 0;
continue 2;
- case 'Bool':
- if (($x[$arg['flag']] & $arg['pow']) === 0) {
- $x[$arg['name']] = false;
- continue 2;
- }
- // no break
default:
if (($x[$arg['flag']] & $arg['pow']) === 0) {
continue 2;
diff --git a/src/Tools.php b/src/Tools.php
index b67317ca7..1287d5ad0 100644
--- a/src/Tools.php
+++ b/src/Tools.php
@@ -705,6 +705,9 @@ abstract class Tools extends AsyncTools
*/
public static function validateEventHandlerClass(string $class): array
{
+ if (!\extension_loaded('tokenizer')) {
+ throw \danog\MadelineProto\Exception::extension('tokenizer');
+ }
$plugin = \is_subclass_of($class, PluginEventHandler::class);
$file = (new ReflectionClass($class))->getFileName();
$code = read($file);
@@ -754,6 +757,14 @@ abstract class Tools extends AsyncTools
$name = $call->name->toLowerString();
if (isset(self::BLOCKING_FUNCTIONS[$name])) {
+ if ($name === 'fopen' &&
+ isset($call->args[0]) &&
+ $call->args[0] instanceof Arg &&
+ $call->args[0]->value instanceof String_ &&
+ \str_starts_with($call->args[0]->value->value, 'php://memory')
+ ) {
+ continue;
+ }
$explanation = self::BLOCKING_FUNCTIONS[$name];
$issues []= new EventHandlerIssue(
message: \sprintf(Lang::$current_lang['do_not_use_blocking_function'], $name, $explanation),
diff --git a/src/Wrappers/DialogHandler.php b/src/Wrappers/DialogHandler.php
index 668e16a05..3f8c0a881 100644
--- a/src/Wrappers/DialogHandler.php
+++ b/src/Wrappers/DialogHandler.php
@@ -25,6 +25,7 @@ use danog\MadelineProto\API;
use danog\MadelineProto\Exception;
use danog\MadelineProto\Settings;
use danog\MadelineProto\Tools;
+use Revolt\EventLoop;
use Throwable;
use Webmozart\Assert\Assert;
@@ -93,7 +94,7 @@ trait DialogHandler
}
$this->cachedAllBotUsers = true;
} finally {
- $lock->release();
+ EventLoop::queue($lock->release(...));
}
}
private function searchRightPts(): void
@@ -188,7 +189,7 @@ trait DialogHandler
try {
$this->getFullDialogsInternal(false);
} finally {
- $lock->release();
+ EventLoop::queue($lock->release(...));
}
return true;
}
diff --git a/tests/testing.php b/tests/testing.php
index 696f6225e..37d3fb26c 100755
--- a/tests/testing.php
+++ b/tests/testing.php
@@ -15,7 +15,9 @@ If not, see .
* Various ways to load MadelineProto.
*/
+use Amp\ByteStream\ReadableBuffer;
use danog\MadelineProto\API;
+use danog\MadelineProto\FileCallback;
use danog\MadelineProto\Logger;
use danog\MadelineProto\Settings;
use danog\MadelineProto\VoIP;
@@ -84,16 +86,6 @@ $MadelineProto = new API(__DIR__.'/../testing.madeline', $settings);
$MadelineProto->start();
$MadelineProto->fileGetContents('https://google.com');
-try {
- $MadelineProto->getSelf();
-} catch (\danog\MadelineProto\Exception $e) {
- if ($e->getMessage() === 'TOS action required, check the logs') {
- $MadelineProto->acceptTos();
- }
-}
-
-//var_dump(count($MadelineProto->getPwrChat('@madelineproto')['participants']));
-
/*
* Test logging
*/
@@ -260,7 +252,7 @@ $media = [];
$media['photo'] = ['_' => 'inputMediaUploadedPhoto', 'file' => __DIR__.'/faust.jpg'];
// Image by URL
-$media['photo'] = ['_' => 'inputMediaPhotoExternal', 'url' => 'https://github.com/danog/MadelineProto/raw/v8/tests/faust.jpg'];
+$media['photo_url'] = ['_' => 'inputMediaPhotoExternal', 'url' => 'https://github.com/danog/MadelineProto/raw/v8/tests/faust.jpg'];
// Sticker
$media['sticker'] = ['_' => 'inputMediaUploadedDocument', 'file' => __DIR__.'/lel.webp', 'attributes' => [['_' => 'documentAttributeSticker', 'alt' => 'LEL']]];
@@ -278,7 +270,7 @@ $media['voice'] = ['_' => 'inputMediaUploadedDocument', 'file' => __DIR__.'/mosc
$media['document'] = ['_' => 'inputMediaUploadedDocument', 'file' => __DIR__.'/60', 'mime_type' => 'magic/magic', 'attributes' => [['_' => 'documentAttributeFilename', 'file_name' => 'magic.magic']]];
// Document by URL
-$media['document'] = ['_' => 'inputMediaDocumentExternal', 'url' => 'https://github.com/danog/MadelineProto/raw/v8/tests/60'];
+$media['document_url'] = ['_' => 'inputMediaDocumentExternal', 'url' => 'https://github.com/danog/MadelineProto/raw/v8/tests/60'];
$message = 'yay '.PHP_VERSION_ID;
$mention = $MadelineProto->getInfo(getenv('TEST_USERNAME')); // Returns an array with all of the constructors that can be extracted from a username or an id
@@ -288,6 +280,60 @@ $peers = json_decode(getenv('TEST_DESTINATION_GROUPS'), true);
if (!$peers) {
die("No TEST_DESTINATION_GROUPS array was provided!");
}
+
+foreach ($media as &$inputMedia) {
+ $inputMedia['content'] = isset($inputMedia['file'])
+ ? read($inputMedia['file'])
+ : $MadelineProto->fileGetContents($inputMedia['url']);
+}
+
+function eq(string $file, string $contents, string $type): void
+{
+ if ($type !== 'photo' && $type !== 'photo_url') {
+ Assert::eq(read($file), $contents, "Not equal $type!");
+ }
+}
+
+function sendMedia(API $MadelineProto, array $media, string $message, string $mention, mixed $peer, string $type): void
+{
+ $medias = [
+ 'base' => $media
+ ];
+ /*if (isset($media['file']) && is_string($media['file'])) {
+ $MadelineProto->sendDocument(
+ peer: $peer,
+ file: new ReadableBuffer(read($media['file'])),
+ callback: fn ($v) => $MadelineProto->logger($v),
+ fileName: basename($media['file'])
+ );
+ $medias['callback'] = array_merge(
+ $media,
+ ['file' => new FileCallback($media['file'], fn ($v) => $MadelineProto->logger(...))]
+ );
+ $medias['stream'] = array_merge(
+ $media,
+ ['file' => new ReadableBuffer(read($media['file']))]
+ );
+ $medias['callback stream'] = array_merge(
+ $media,
+ ['file' => new FileCallback(new ReadableBuffer(read($media['file'])), fn ($v) => $MadelineProto->logger(...))]
+ );
+ } elseif (isset($media['url'])) {
+ $medias['callback'] = array_merge(
+ $media,
+ ['url' => new FileCallback($media['url'], fn ($v) => $MadelineProto->logger(...))]
+ );
+ }*/
+ foreach ($medias as $subtype => $m) {
+ $MadelineProto->logger("Sending $type $subtype");
+ $dl = $MadelineProto->extractMessage($MadelineProto->messages->sendMedia(['peer' => $peer, 'media' => $m, 'message' => '['.$message.'](mention:'.$mention.')', 'parse_mode' => 'markdown']));
+
+ $MadelineProto->logger("Downloading $type $subtype");
+ $file = $MadelineProto->downloadToDir($dl, '/tmp');
+ eq($file, $m['content'], $type);
+ }
+}
+
foreach ($peers as $peer) {
$sentMessage = $MadelineProto->messages->sendMessage(['peer' => $peer, 'message' => $message, 'entities' => [['_' => 'inputMessageEntityMentionName', 'offset' => 0, 'length' => mb_strlen($message), 'user_id' => $mention]]]);
$MadelineProto->logger($sentMessage, Logger::NOTICE);
@@ -303,26 +349,15 @@ foreach ($peers as $peer) {
['_' => 'inputSingleMedia', 'media' => $inputMedia, 'message' => '['.$message.'](mention:'.$mention.')', 'parse_mode' => 'markdown'],
]]);
}
- $fileOrig = isset($inputMedia['file'])
- ? read($inputMedia['file'])
- : $MadelineProto->fileGetContents($inputMedia['url']);
- $MadelineProto->logger("Sending $type");
- $dl = $MadelineProto->extractMessage($MadelineProto->messages->sendMedia(['peer' => $peer, 'media' => $inputMedia, 'message' => '['.$message.'](mention:'.$mention.')', 'parse_mode' => 'markdown']));
- $MadelineProto->logger("Downloading $type");
- $file = $MadelineProto->downloadToDir($dl, '/tmp');
- if ($type !== 'photo') {
- Assert::eq(read($file), $fileOrig, "Not equal!");
- }
+ sendMedia($MadelineProto, $inputMedia, $message, $mention, $peer, $type);
$MadelineProto->logger("Uploading $type");
$media = $MadelineProto->messages->uploadMedia(['peer' => '@me', 'media' => $inputMedia]);
$MadelineProto->logger("Downloading $type");
$file = $MadelineProto->downloadToDir($media, '/tmp');
- if ($type !== 'photo') {
- Assert::eq(read($file), $fileOrig, "Not equal!");
- }
+ eq($file, $inputMedia['content'], $type);
$MadelineProto->logger("Re-sending $type");
$inputMedia['file'] = $media;
@@ -331,9 +366,7 @@ foreach ($peers as $peer) {
$MadelineProto->logger("Re-downloading $type");
$file = $MadelineProto->downloadToDir($dl, '/tmp');
- if ($type !== 'photo') {
- Assert::eq(read($file), $fileOrig, "Not equal!");
- }
+ eq($file, $inputMedia['content'], $type);
}
}