1
0
mirror of https://github.com/danog/MadelineProto.git synced 2025-01-22 22:51:11 +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.",
"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,
$settings,
$forceFull
)->await(Tools::getTimeoutCancellation(30.0));
)->await(Tools::getTimeoutCancellation(3.0));
} catch (CancelledException $e) {
if (!$e->getPrevious() instanceof TimeoutException) {
throw $e;
@ -322,16 +322,10 @@ final class API extends AbstractAPI
if ($unserialized === 0) {
// Timeout
Logger::log('!!! Could not connect to MadelineProto, please check and report the logs for more details. !!!', Logger::FATAL_ERROR);
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);
throw new Exception(Lang::$current_lang['could_not_connect_to_MadelineProto']);
} elseif ($unserialized instanceof Throwable) {
// IPC server error, try fetching full session
return $this->connectToMadelineProto($settings, true);
// IPC server error
throw $unserialized;
} elseif ($unserialized instanceof ChannelledSocket) {
// Success, IPC client
$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->chatId = $info['bot_api_id'];
$this->senderId = isset($rawMessage['from_id'])
? $this->API->getIdInternal($rawMessage['from_id'])
? $this->getClient()->getIdInternal($rawMessage['from_id'])
: $this->chatId;
$this->date = $rawMessage['date'];
$this->mentioned = $rawMessage['mentioned'];
@ -131,14 +131,14 @@ abstract class AbstractMessage extends Update implements SimpleFilters
}
return $this->replyCache;
}
$messages = $this->API->methodCallAsyncRead(
$messages = $this->getClient()->methodCallAsyncRead(
API::isSupergroup($this->chatId) ? 'channels.getMessages' : 'messages.getMessages',
[
'channel' => $this->chatId,
'id' => [['_' => 'inputMessageReplyTo', 'id' => $this->id]]
]
)['messages'];
$this->replyCache = $messages ? $this->API->wrapMessage($messages[0]) : null;
$this->replyCache = $messages ? $this->getClient()->wrapMessage($messages[0]) : null;
$this->replyCached = true;
if (!$this->replyCache instanceof $class) {
return null;
@ -153,7 +153,7 @@ abstract class AbstractMessage extends Update implements SimpleFilters
*/
public function delete(bool $revoke = true): void
{
$this->API->methodCallAsyncRead(
$this->getClient()->methodCallAsyncRead(
API::isSupergroup($this->chatId) ? 'channels.deleteMessages' : 'messages.deleteMessages',
[
'channel' => $this->chatId,
@ -191,7 +191,7 @@ abstract class AbstractMessage extends Update implements SimpleFilters
bool $noWebpage = false,
bool $updateStickersetsOrder = false,
): Message {
return $this->API->sendMessage(
return $this->getClient()->sendMessage(
peer: $this->chatId,
message: $message,
parseMode: $parseMode,

View File

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

View File

@ -95,13 +95,13 @@ abstract class Message extends AbstractMessage
$this->fwdInfo = new ForwardedInfo(
$fwdFrom['date'],
isset($fwdFrom['from_id'])
? $this->API->getIdInternal($fwdFrom['from_id'])
? $this->getClient()->getIdInternal($fwdFrom['from_id'])
: null,
$fwdFrom['from_name'] ?? null,
$fwdFrom['channel_post'] ?? null,
$fwdFrom['post_author'] ?? null,
isset($fwdFrom['saved_from_peer'])
? $this->API->getIdInternal($fwdFrom['saved_from_peer'])
? $this->getClient()->getIdInternal($fwdFrom['saved_from_peer'])
: 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\FileCallbackInterface;
use danog\MadelineProto\Logger;
use danog\MadelineProto\MTProto;
use danog\MadelineProto\MTProtoTools\FilesLogic;
use danog\MadelineProto\SessionPaths;
use danog\MadelineProto\Tools;
@ -55,9 +56,9 @@ final class Client extends ClientAbstract
/**
* 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
{
return $this->API->callPluginMethod(
return $this->getClient()->callPluginMethod(
$this->__plugin,
$name,
$arguments
@ -23,18 +23,18 @@ final class EventHandlerProxy extends IpcCapable
}
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
{
$this->API->setPluginProperty($this->__plugin, $name, $value);
$this->getClient()->setPluginProperty($this->__plugin, $name, $value);
}
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
{
$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
{
private readonly string $session;
protected MTProto|Client|null $API;
protected readonly string $session;
private MTProto|Client|null $API;
/** @internal */
protected function __construct(
MTProto|Client $API,
) {
protected function __construct(MTProto $API)
{
$this->API = $API;
if ($API instanceof MTProto) {
$this->session = $API->wrapper->getSession()->getSessionDirectoryPath();
} else {
$this->session = $API->getSession()->getSessionDirectoryPath();
}
$this->session = $API->getSessionName();
}
/** @internal */
public function __sleep()
final public function __sleep()
{
$vars = \get_object_vars($this);
unset($vars['API']);
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);
unset($vars['API']);

View File

@ -419,6 +419,16 @@ final class MTProto implements TLCallback, LoggerGetter
'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.
*
@ -482,8 +492,8 @@ final class MTProto implements TLCallback, LoggerGetter
public function __construct(Settings|SettingsEmpty $settings, ?APIWrapper $wrapper = null)
{
if ($wrapper) {
self::$references[\spl_object_hash($this)] = $this;
$this->wrapper = $wrapper;
self::$references[$this->getSessionName()] = $this;
}
$initDeferred = new DeferredFuture;
@ -974,10 +984,10 @@ final class MTProto implements TLCallback, LoggerGetter
// Setup one-time stuffs
Magic::start(light: false);
// Set reference to itself
self::$references[\spl_object_hash($this)] = $this;
// Set API wrapper
$this->wrapper = $wrapper;
// Set reference to itself
self::$references[$this->getSessionName()] = $this;
$deferred = new DeferredFuture;
$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->logger('Will unreference instance');
if (isset(self::$references[\spl_object_hash($this)])) {
unset(self::$references[\spl_object_hash($this)]);
if (isset(self::$references[$this->getSessionName()])) {
unset(self::$references[$this->getSessionName()]);
}
$this->stopLoops();
if (isset($this->seqUpdater)) {

View File

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

View File

@ -21,7 +21,7 @@ declare(strict_types=1);
namespace danog\MadelineProto\TL\Types;
use ArrayAccess;
use danog\MadelineProto\Ipc\Client;
use danog\MadelineProto\Ipc\IpcCapable;
use danog\MadelineProto\MTProto;
use JsonSerializable;
@ -30,7 +30,7 @@ use JsonSerializable;
*
* @implements ArrayAccess<array-key, mixed>
*/
final class Button implements JsonSerializable, ArrayAccess
final class Button extends IpcCapable implements JsonSerializable, ArrayAccess
{
/** Button label */
public readonly string $label;
@ -40,15 +40,6 @@ final class Button implements JsonSerializable, ArrayAccess
* @var array<array-key, mixed>
*/
private array $button = [];
/**
* Session name.
*/
private string $session = '';
/**
* MTProto instance.
*
*/
private MTProto|Client|null $API = null;
/**
* Message ID.
*/
@ -69,6 +60,7 @@ final class Button implements JsonSerializable, ArrayAccess
*/
public function __construct(MTProto $API, array $message, array $button)
{
parent::__construct($API);
if (!isset($message['from_id']) // No other option
// It's a channel/chat, 100% what we need
|| $message['peer_id']['_'] !== 'peerUser'
@ -82,15 +74,6 @@ final class Button implements JsonSerializable, ArrayAccess
$this->label = $button['text'];
$this->button = $button;
$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.
@ -99,18 +82,17 @@ final class Button implements JsonSerializable, ArrayAccess
*/
public function click(bool $donotwait = true)
{
$this->API ??= Client::giveInstanceBySession($this->session);
switch ($this->button['_']) {
default:
return false;
case 'keyboardButtonUrl':
return $this->button['url'];
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':
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':
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\RendererStyle\RendererStyle;
use BaconQrCode\Writer;
use danog\MadelineProto\Ipc\Client;
use danog\MadelineProto\Ipc\IpcCapable;
use danog\MadelineProto\MTProto;
use danog\MadelineProto\Tools;
use JsonSerializable;
@ -38,27 +38,17 @@ use JsonSerializable;
/**
* Represents a login QR code.
*/
final class LoginQrCode implements JsonSerializable
final class LoginQrCode extends IpcCapable implements JsonSerializable
{
private string $session;
/** @internal */
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) */
public readonly string $link,
/** @var positive-int The expiry date of the link */
public readonly int $expiry
) {
$this->session = $API->getWrapper()->getSession()->getSessionDirectoryPath();
}
/**
* @internal
*/
public function __sleep(): array
{
return ['link', 'expiry', 'session'];
parent::__construct($API);
}
/** @internal */
@ -95,8 +85,7 @@ final class LoginQrCode implements JsonSerializable
public function getLoginCancellation(): Cancellation
{
$this->API ??= Client::giveInstanceBySession($this->session);
return $this->API->getQrLoginCancellation();
return $this->getClient()->getQrLoginCancellation();
}
/**
@ -126,7 +115,7 @@ final class LoginQrCode implements JsonSerializable
(new DeferredFuture)->getFuture()->await($cancellation);
} catch (CancelledException) {
$customCancellation?->throwIfRequested();
return $this->API->qrLogin();
return $this->getClient()->qrLogin();
}
throw new AssertionError("Unreachable!");
}