mirror of
https://github.com/danog/MadelineProto.git
synced 2024-12-02 23:07:45 +01:00
Merge branch 'v8' into v8
This commit is contained in:
commit
0e27befbf8
@ -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)
|
||||
|
2
docs
2
docs
@ -1 +1 @@
|
||||
Subproject commit 61cdff1c445fd53456b63d33a6feac12e4023514
|
||||
Subproject commit dbfd462edb6a96c6bb1214b5def5e350a669100e
|
@ -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(...));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -908,6 +908,10 @@
|
||||
<code>copy</code>
|
||||
<code>unwrap</code>
|
||||
</MissingReturnType>
|
||||
<PossiblyUndefinedArrayOffset>
|
||||
<code>$payload[0]</code>
|
||||
<code>$payload[0]</code>
|
||||
</PossiblyUndefinedArrayOffset>
|
||||
<PropertyNotSetInConstructor>
|
||||
<code>$data</code>
|
||||
<code>Wrapper</code>
|
||||
@ -1508,6 +1512,9 @@
|
||||
<code>seek</code>
|
||||
<code>seek</code>
|
||||
</PossiblyUndefinedMethod>
|
||||
<PossiblyUndefinedVariable>
|
||||
<code>$l</code>
|
||||
</PossiblyUndefinedVariable>
|
||||
</file>
|
||||
<file src="src/MTProtoTools/MinDatabase.php">
|
||||
<MissingReturnType>
|
||||
|
@ -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.
|
||||
*
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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') {
|
||||
|
@ -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];
|
||||
|
@ -197,7 +197,7 @@ final class DataCenterConnection implements JsonSerializable
|
||||
$this->syncAuthorization();
|
||||
}
|
||||
} finally {
|
||||
$lock->release();
|
||||
EventLoop::queue($lock->release(...));
|
||||
}
|
||||
if ($this->hasTempAuthKey()) {
|
||||
$connection->pingHttpWaiter();
|
||||
|
@ -157,7 +157,7 @@ final class CacheContainer
|
||||
$this->ttl = $newTtl;
|
||||
$this->cache = $newValues;
|
||||
} finally {
|
||||
$lock->release();
|
||||
EventLoop::queue($lock->release(...));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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];
|
||||
|
@ -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];
|
||||
|
@ -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];
|
||||
|
@ -263,7 +263,7 @@ abstract class EventHandler extends AbstractAPI
|
||||
} finally {
|
||||
$this->startDeferred = null;
|
||||
$startDeferred->complete();
|
||||
$lock->release();
|
||||
EventLoop::queue($lock->release(...));
|
||||
}
|
||||
}
|
||||
/**
|
||||
|
@ -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' ],
|
||||
|
@ -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]);
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -62,6 +62,6 @@ trait ClosableTrait
|
||||
|
||||
final public function __destruct()
|
||||
{
|
||||
$this->close();
|
||||
EventLoop::queue($this->close(...));
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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());
|
||||
|
@ -1670,7 +1670,7 @@ final class MTProto implements TLCallback, LoggerGetter
|
||||
$this->logger->logger('Reported!');
|
||||
}
|
||||
} finally {
|
||||
$lock->release();
|
||||
EventLoop::queue($lock->release(...));
|
||||
}
|
||||
}
|
||||
/**
|
||||
|
@ -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'])) {
|
||||
|
@ -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':
|
||||
|
@ -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(...));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
/**
|
||||
|
@ -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;
|
||||
|
@ -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 */
|
||||
|
@ -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)
|
||||
|
@ -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(...));
|
||||
}
|
||||
}
|
||||
/**
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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),
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -15,7 +15,9 @@ If not, see <http://www.gnu.org/licenses/>.
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user