1
0
mirror of https://github.com/danog/MadelineProto.git synced 2025-01-23 02:11:14 +01:00

Remove non-IPC fallback, improve IPC logic

This commit is contained in:
Daniil Gentili 2023-07-25 18:44:30 +02:00
parent b2446fd4d7
commit 76446a2d82
Signed by: danog
GPG Key ID: 8C1BE3B34B230CA7
13 changed files with 62 additions and 88 deletions

2
docs

@ -1 +1 @@
Subproject commit 31abc3950cba076c9db6c79f209203ac6cd35cad Subproject commit 84c66b69ecdb6407ce254f1d901a253f6b6c00a0

View File

@ -159,5 +159,7 @@
"account_banned": "!!!!!!! WARNING !!!!!!!\nTelegram's flood prevention system suspended this account.\nTo continue, manual verification is required.\nSend an email to recover@telegram.org, asking to unban the phone number %s, and shortly describe what will you do with this phone number.\nThen login again.\nIf you intentionally deleted this account, ignore this message.", "account_banned": "!!!!!!! WARNING !!!!!!!\nTelegram's flood prevention system suspended this account.\nTo continue, manual verification is required.\nSend an email to recover@telegram.org, asking to unban the phone number %s, and shortly describe what will you do with this phone number.\nThen login again.\nIf you intentionally deleted this account, ignore this message.",
"plugin_path_does_not_exist": "Plugin path %s does not exist!" "plugin_path_does_not_exist": "Plugin path %s does not exist!",
"could_not_connect_to_MadelineProto": "Could not connect to MadelineProto, please enable proc_open or disable webserver path rewrites to fix!"
} }

View File

@ -311,7 +311,7 @@ final class API extends AbstractAPI
$this->session, $this->session,
$settings, $settings,
$forceFull $forceFull
)->await(Tools::getTimeoutCancellation(30.0)); )->await(Tools::getTimeoutCancellation(3.0));
} catch (CancelledException $e) { } catch (CancelledException $e) {
if (!$e->getPrevious() instanceof TimeoutException) { if (!$e->getPrevious() instanceof TimeoutException) {
throw $e; throw $e;
@ -322,16 +322,10 @@ final class API extends AbstractAPI
if ($unserialized === 0) { if ($unserialized === 0) {
// Timeout // Timeout
Logger::log('!!! Could not connect to MadelineProto, please check and report the logs for more details. !!!', Logger::FATAL_ERROR); throw new Exception(Lang::$current_lang['could_not_connect_to_MadelineProto']);
if (!$tryReconnect || (\defined('MADELINEPROTO_TEST') && \constant('MADELINEPROTO_TEST') === 'testing')) {
throw new Exception('Could not connect to MadelineProto, please check the MadelineProto.log file to debug!');
}
Logger::log('!!! Reconnecting using slower method. !!!', Logger::FATAL_ERROR);
// IPC server error, try fetching full session
return $this->connectToMadelineProto($settings, true, false);
} elseif ($unserialized instanceof Throwable) { } elseif ($unserialized instanceof Throwable) {
// IPC server error, try fetching full session // IPC server error
return $this->connectToMadelineProto($settings, true); throw $unserialized;
} elseif ($unserialized instanceof ChannelledSocket) { } elseif ($unserialized instanceof ChannelledSocket) {
// Success, IPC client // Success, IPC client
$this->wrapper->setAPI(new Client($unserialized, $this->session, Logger::$default)); $this->wrapper->setAPI(new Client($unserialized, $this->session, Logger::$default));

View File

@ -53,7 +53,7 @@ abstract class AbstractMessage extends Update implements SimpleFilters
$this->id = $rawMessage['id']; $this->id = $rawMessage['id'];
$this->chatId = $info['bot_api_id']; $this->chatId = $info['bot_api_id'];
$this->senderId = isset($rawMessage['from_id']) $this->senderId = isset($rawMessage['from_id'])
? $this->API->getIdInternal($rawMessage['from_id']) ? $this->getClient()->getIdInternal($rawMessage['from_id'])
: $this->chatId; : $this->chatId;
$this->date = $rawMessage['date']; $this->date = $rawMessage['date'];
$this->mentioned = $rawMessage['mentioned']; $this->mentioned = $rawMessage['mentioned'];
@ -131,14 +131,14 @@ abstract class AbstractMessage extends Update implements SimpleFilters
} }
return $this->replyCache; return $this->replyCache;
} }
$messages = $this->API->methodCallAsyncRead( $messages = $this->getClient()->methodCallAsyncRead(
API::isSupergroup($this->chatId) ? 'channels.getMessages' : 'messages.getMessages', API::isSupergroup($this->chatId) ? 'channels.getMessages' : 'messages.getMessages',
[ [
'channel' => $this->chatId, 'channel' => $this->chatId,
'id' => [['_' => 'inputMessageReplyTo', 'id' => $this->id]] 'id' => [['_' => 'inputMessageReplyTo', 'id' => $this->id]]
] ]
)['messages']; )['messages'];
$this->replyCache = $messages ? $this->API->wrapMessage($messages[0]) : null; $this->replyCache = $messages ? $this->getClient()->wrapMessage($messages[0]) : null;
$this->replyCached = true; $this->replyCached = true;
if (!$this->replyCache instanceof $class) { if (!$this->replyCache instanceof $class) {
return null; return null;
@ -153,7 +153,7 @@ abstract class AbstractMessage extends Update implements SimpleFilters
*/ */
public function delete(bool $revoke = true): void public function delete(bool $revoke = true): void
{ {
$this->API->methodCallAsyncRead( $this->getClient()->methodCallAsyncRead(
API::isSupergroup($this->chatId) ? 'channels.deleteMessages' : 'messages.deleteMessages', API::isSupergroup($this->chatId) ? 'channels.deleteMessages' : 'messages.deleteMessages',
[ [
'channel' => $this->chatId, 'channel' => $this->chatId,
@ -191,7 +191,7 @@ abstract class AbstractMessage extends Update implements SimpleFilters
bool $noWebpage = false, bool $noWebpage = false,
bool $updateStickersetsOrder = false, bool $updateStickersetsOrder = false,
): Message { ): Message {
return $this->API->sendMessage( return $this->getClient()->sendMessage(
peer: $this->chatId, peer: $this->chatId,
message: $message, message: $message,
parseMode: $parseMode, parseMode: $parseMode,

View File

@ -82,7 +82,7 @@ abstract class Media extends IpcCapable implements JsonSerializable
*/ */
public function getDownloadLink(?string $scriptUrl = null): string public function getDownloadLink(?string $scriptUrl = null): string
{ {
return $this->API->getDownloadLink($this, $scriptUrl); return $this->getClient()->getDownloadLink($this, $scriptUrl);
} }
/** @internal */ /** @internal */

View File

@ -95,13 +95,13 @@ abstract class Message extends AbstractMessage
$this->fwdInfo = new ForwardedInfo( $this->fwdInfo = new ForwardedInfo(
$fwdFrom['date'], $fwdFrom['date'],
isset($fwdFrom['from_id']) isset($fwdFrom['from_id'])
? $this->API->getIdInternal($fwdFrom['from_id']) ? $this->getClient()->getIdInternal($fwdFrom['from_id'])
: null, : null,
$fwdFrom['from_name'] ?? null, $fwdFrom['from_name'] ?? null,
$fwdFrom['channel_post'] ?? null, $fwdFrom['channel_post'] ?? null,
$fwdFrom['post_author'] ?? null, $fwdFrom['post_author'] ?? null,
isset($fwdFrom['saved_from_peer']) isset($fwdFrom['saved_from_peer'])
? $this->API->getIdInternal($fwdFrom['saved_from_peer']) ? $this->getClient()->getIdInternal($fwdFrom['saved_from_peer'])
: null, : null,
$fwdFrom['saved_from_msg_id'] ?? null $fwdFrom['saved_from_msg_id'] ?? null
); );

View File

@ -27,6 +27,7 @@ use Amp\Ipc\Sync\ChannelledSocket;
use danog\MadelineProto\Exception; use danog\MadelineProto\Exception;
use danog\MadelineProto\FileCallbackInterface; use danog\MadelineProto\FileCallbackInterface;
use danog\MadelineProto\Logger; use danog\MadelineProto\Logger;
use danog\MadelineProto\MTProto;
use danog\MadelineProto\MTProtoTools\FilesLogic; use danog\MadelineProto\MTProtoTools\FilesLogic;
use danog\MadelineProto\SessionPaths; use danog\MadelineProto\SessionPaths;
use danog\MadelineProto\Tools; use danog\MadelineProto\Tools;
@ -55,9 +56,9 @@ final class Client extends ClientAbstract
/** /**
* Returns an instance of a client by session name. * Returns an instance of a client by session name.
*/ */
public static function giveInstanceBySession(string $session): Client public static function giveInstanceBySession(string $session): Client|MTProto
{ {
return self::$instances[$session]; return self::$instances[$session] ?? MTProto::giveInstanceBySession($session);
} }
/** /**

View File

@ -15,7 +15,7 @@ final class EventHandlerProxy extends IpcCapable
} }
public function __call(string $name, array $arguments): mixed public function __call(string $name, array $arguments): mixed
{ {
return $this->API->callPluginMethod( return $this->getClient()->callPluginMethod(
$this->__plugin, $this->__plugin,
$name, $name,
$arguments $arguments
@ -23,18 +23,18 @@ final class EventHandlerProxy extends IpcCapable
} }
public function __get(string $name): mixed public function __get(string $name): mixed
{ {
return $this->API->getPluginProperty($this->__plugin, $name); return $this->getClient()->getPluginProperty($this->__plugin, $name);
} }
public function __set(string $name, mixed $value): void public function __set(string $name, mixed $value): void
{ {
$this->API->setPluginProperty($this->__plugin, $name, $value); $this->getClient()->setPluginProperty($this->__plugin, $name, $value);
} }
public function __isset(string $name): bool public function __isset(string $name): bool
{ {
return $this->API->issetPluginProperty($this->__plugin, $name); return $this->getClient()->issetPluginProperty($this->__plugin, $name);
} }
public function __unset(string $name): void public function __unset(string $name): void
{ {
$this->API->unsetPluginProperty($this->__plugin, $name); $this->getClient()->unsetPluginProperty($this->__plugin, $name);
} }
} }

View File

@ -11,35 +11,30 @@ use danog\MadelineProto\MTProto;
*/ */
abstract class IpcCapable abstract class IpcCapable
{ {
private readonly string $session; protected readonly string $session;
protected MTProto|Client|null $API; private MTProto|Client|null $API;
/** @internal */ /** @internal */
protected function __construct( protected function __construct(MTProto $API)
MTProto|Client $API, {
) {
$this->API = $API; $this->API = $API;
if ($API instanceof MTProto) { $this->session = $API->getSessionName();
$this->session = $API->wrapper->getSession()->getSessionDirectoryPath();
} else {
$this->session = $API->getSession()->getSessionDirectoryPath();
}
} }
/** @internal */ /** @internal */
public function __sleep() final public function __sleep()
{ {
$vars = \get_object_vars($this); $vars = \get_object_vars($this);
unset($vars['API']); unset($vars['API']);
return \array_keys($vars); return \array_keys($vars);
} }
/** @internal */
public function __wakeup(): void final protected function getClient(): MTProto|Client
{ {
$this->API = Client::giveInstanceBySession($this->session); return $this->API ??= Client::giveInstanceBySession($this->session);
} }
public function __debugInfo() final public function __debugInfo()
{ {
$vars = \get_object_vars($this); $vars = \get_object_vars($this);
unset($vars['API']); unset($vars['API']);

View File

@ -419,6 +419,16 @@ final class MTProto implements TLCallback, LoggerGetter
'session' => ['innerMadelineProto' => true, 'enableCache' => false], 'session' => ['innerMadelineProto' => true, 'enableCache' => false],
]; ];
/**
* Returns an instance of a client by session name.
*
* @internal
*/
public static function giveInstanceBySession(string $session): MTProto
{
return self::$references[$session];
}
/** /**
* Serialize session, returning object to serialize to db. * Serialize session, returning object to serialize to db.
* *
@ -482,8 +492,8 @@ final class MTProto implements TLCallback, LoggerGetter
public function __construct(Settings|SettingsEmpty $settings, ?APIWrapper $wrapper = null) public function __construct(Settings|SettingsEmpty $settings, ?APIWrapper $wrapper = null)
{ {
if ($wrapper) { if ($wrapper) {
self::$references[\spl_object_hash($this)] = $this;
$this->wrapper = $wrapper; $this->wrapper = $wrapper;
self::$references[$this->getSessionName()] = $this;
} }
$initDeferred = new DeferredFuture; $initDeferred = new DeferredFuture;
@ -974,10 +984,10 @@ final class MTProto implements TLCallback, LoggerGetter
// Setup one-time stuffs // Setup one-time stuffs
Magic::start(light: false); Magic::start(light: false);
// Set reference to itself
self::$references[\spl_object_hash($this)] = $this;
// Set API wrapper // Set API wrapper
$this->wrapper = $wrapper; $this->wrapper = $wrapper;
// Set reference to itself
self::$references[$this->getSessionName()] = $this;
$deferred = new DeferredFuture; $deferred = new DeferredFuture;
$this->initPromise = $deferred->getFuture(); $this->initPromise = $deferred->getFuture();
@ -1072,8 +1082,8 @@ final class MTProto implements TLCallback, LoggerGetter
$this->logger = new Logger(new \danog\MadelineProto\Settings\Logger); $this->logger = new Logger(new \danog\MadelineProto\Settings\Logger);
} }
$this->logger->logger('Will unreference instance'); $this->logger->logger('Will unreference instance');
if (isset(self::$references[\spl_object_hash($this)])) { if (isset(self::$references[$this->getSessionName()])) {
unset(self::$references[\spl_object_hash($this)]); unset(self::$references[$this->getSessionName()]);
} }
$this->stopLoops(); $this->stopLoops();
if (isset($this->seqUpdater)) { if (isset($this->seqUpdater)) {

View File

@ -35,6 +35,7 @@ use Revolt\EventLoop;
use Throwable; use Throwable;
use const LOCK_EX; use const LOCK_EX;
use function Amp\File\exists; use function Amp\File\exists;
use function Amp\Ipc\connect; use function Amp\Ipc\connect;

View File

@ -21,7 +21,7 @@ declare(strict_types=1);
namespace danog\MadelineProto\TL\Types; namespace danog\MadelineProto\TL\Types;
use ArrayAccess; use ArrayAccess;
use danog\MadelineProto\Ipc\Client; use danog\MadelineProto\Ipc\IpcCapable;
use danog\MadelineProto\MTProto; use danog\MadelineProto\MTProto;
use JsonSerializable; use JsonSerializable;
@ -30,7 +30,7 @@ use JsonSerializable;
* *
* @implements ArrayAccess<array-key, mixed> * @implements ArrayAccess<array-key, mixed>
*/ */
final class Button implements JsonSerializable, ArrayAccess final class Button extends IpcCapable implements JsonSerializable, ArrayAccess
{ {
/** Button label */ /** Button label */
public readonly string $label; public readonly string $label;
@ -40,15 +40,6 @@ final class Button implements JsonSerializable, ArrayAccess
* @var array<array-key, mixed> * @var array<array-key, mixed>
*/ */
private array $button = []; private array $button = [];
/**
* Session name.
*/
private string $session = '';
/**
* MTProto instance.
*
*/
private MTProto|Client|null $API = null;
/** /**
* Message ID. * Message ID.
*/ */
@ -69,6 +60,7 @@ final class Button implements JsonSerializable, ArrayAccess
*/ */
public function __construct(MTProto $API, array $message, array $button) public function __construct(MTProto $API, array $message, array $button)
{ {
parent::__construct($API);
if (!isset($message['from_id']) // No other option if (!isset($message['from_id']) // No other option
// It's a channel/chat, 100% what we need // It's a channel/chat, 100% what we need
|| $message['peer_id']['_'] !== 'peerUser' || $message['peer_id']['_'] !== 'peerUser'
@ -82,15 +74,6 @@ final class Button implements JsonSerializable, ArrayAccess
$this->label = $button['text']; $this->label = $button['text'];
$this->button = $button; $this->button = $button;
$this->id = $message['id']; $this->id = $message['id'];
$this->API = $API;
$this->session = $API->getWrapper()->getSession()->getSessionDirectoryPath();
}
/**
* Sleep function.
*/
public function __sleep(): array
{
return ['button', 'peer', 'id', 'session'];
} }
/** /**
* Click on button. * Click on button.
@ -99,18 +82,17 @@ final class Button implements JsonSerializable, ArrayAccess
*/ */
public function click(bool $donotwait = true) public function click(bool $donotwait = true)
{ {
$this->API ??= Client::giveInstanceBySession($this->session);
switch ($this->button['_']) { switch ($this->button['_']) {
default: default:
return false; return false;
case 'keyboardButtonUrl': case 'keyboardButtonUrl':
return $this->button['url']; return $this->button['url'];
case 'keyboardButton': case 'keyboardButton':
return $this->API->clickInternal($donotwait, 'messages.sendMessage', ['peer' => $this->peer, 'message' => $this->button['text'], 'reply_to_msg_id' => $this->id]); return $this->getClient()->clickInternal($donotwait, 'messages.sendMessage', ['peer' => $this->peer, 'message' => $this->button['text'], 'reply_to_msg_id' => $this->id]);
case 'keyboardButtonCallback': case 'keyboardButtonCallback':
return $this->API->clickInternal($donotwait, 'messages.getBotCallbackAnswer', ['peer' => $this->peer, 'msg_id' => $this->id, 'data' => $this->button['data']]); return $this->getClient()->clickInternal($donotwait, 'messages.getBotCallbackAnswer', ['peer' => $this->peer, 'msg_id' => $this->id, 'data' => $this->button['data']]);
case 'keyboardButtonGame': case 'keyboardButtonGame':
return $this->API->clickInternal($donotwait, 'messages.getBotCallbackAnswer', ['peer' => $this->peer, 'msg_id' => $this->id, 'game' => true]); return $this->getClient()->clickInternal($donotwait, 'messages.getBotCallbackAnswer', ['peer' => $this->peer, 'msg_id' => $this->id, 'game' => true]);
} }
} }
/** /**

View File

@ -30,7 +30,7 @@ use BaconQrCode\Renderer\ImageRenderer;
use BaconQrCode\Renderer\PlainTextRenderer; use BaconQrCode\Renderer\PlainTextRenderer;
use BaconQrCode\Renderer\RendererStyle\RendererStyle; use BaconQrCode\Renderer\RendererStyle\RendererStyle;
use BaconQrCode\Writer; use BaconQrCode\Writer;
use danog\MadelineProto\Ipc\Client; use danog\MadelineProto\Ipc\IpcCapable;
use danog\MadelineProto\MTProto; use danog\MadelineProto\MTProto;
use danog\MadelineProto\Tools; use danog\MadelineProto\Tools;
use JsonSerializable; use JsonSerializable;
@ -38,27 +38,17 @@ use JsonSerializable;
/** /**
* Represents a login QR code. * Represents a login QR code.
*/ */
final class LoginQrCode implements JsonSerializable final class LoginQrCode extends IpcCapable implements JsonSerializable
{ {
private string $session;
/** @internal */ /** @internal */
public function __construct( public function __construct(
private MTProto|Client $API, MTProto $API,
/** @var non-empty-string The [QR code login link](https://core.telegram.org/api/links#qr-code-login-links) */ /** @var non-empty-string The [QR code login link](https://core.telegram.org/api/links#qr-code-login-links) */
public readonly string $link, public readonly string $link,
/** @var positive-int The expiry date of the link */ /** @var positive-int The expiry date of the link */
public readonly int $expiry public readonly int $expiry
) { ) {
$this->session = $API->getWrapper()->getSession()->getSessionDirectoryPath(); parent::__construct($API);
}
/**
* @internal
*/
public function __sleep(): array
{
return ['link', 'expiry', 'session'];
} }
/** @internal */ /** @internal */
@ -95,8 +85,7 @@ final class LoginQrCode implements JsonSerializable
public function getLoginCancellation(): Cancellation public function getLoginCancellation(): Cancellation
{ {
$this->API ??= Client::giveInstanceBySession($this->session); return $this->getClient()->getQrLoginCancellation();
return $this->API->getQrLoginCancellation();
} }
/** /**
@ -126,7 +115,7 @@ final class LoginQrCode implements JsonSerializable
(new DeferredFuture)->getFuture()->await($cancellation); (new DeferredFuture)->getFuture()->await($cancellation);
} catch (CancelledException) { } catch (CancelledException) {
$customCancellation?->throwIfRequested(); $customCancellation?->throwIfRequested();
return $this->API->qrLogin(); return $this->getClient()->qrLogin();
} }
throw new AssertionError("Unreachable!"); throw new AssertionError("Unreachable!");
} }