diff --git a/src/danog/MadelineProto/API.php b/src/danog/MadelineProto/API.php index b9fe19c7..ef84e484 100644 --- a/src/danog/MadelineProto/API.php +++ b/src/danog/MadelineProto/API.php @@ -20,6 +20,7 @@ namespace danog\MadelineProto; use Amp\Ipc\Sync\ChannelledSocket; +use Amp\Loop; use danog\MadelineProto\Ipc\Client; use danog\MadelineProto\Ipc\Server; use danog\MadelineProto\Settings\Logger as SettingsLogger; @@ -159,6 +160,7 @@ class API extends InternalDoc $this->APIFactory(); $this->logger->logger(Lang::$current_lang['madelineproto_ready'], Logger::NOTICE); } + /** * Reconnect to full instance. * @@ -276,7 +278,7 @@ class API extends InternalDoc } } } - $this->methods = self::getInternalMethodList($this->API); + $this->methods = self::getInternalMethodList($this->API, MTProto::class); } } diff --git a/src/danog/MadelineProto/APIWrapper.php b/src/danog/MadelineProto/APIWrapper.php index 76810b91..24a21f27 100644 --- a/src/danog/MadelineProto/APIWrapper.php +++ b/src/danog/MadelineProto/APIWrapper.php @@ -188,7 +188,10 @@ final class APIWrapper yield from $this->API->initAsynchronously(); } - yield from $this->session->serialize($this, $this->session->getSessionPath()); + yield from $this->session->serialize( + yield from $this->API->serializeSession($this), + $this->session->getSessionPath() + ); if ($this->API) { yield from $this->session->storeLightState($this->API); diff --git a/src/danog/MadelineProto/AbstractAPIFactory.php b/src/danog/MadelineProto/AbstractAPIFactory.php index 7e665009..2a8df9cc 100644 --- a/src/danog/MadelineProto/AbstractAPIFactory.php +++ b/src/danog/MadelineProto/AbstractAPIFactory.php @@ -200,8 +200,8 @@ abstract class AbstractAPIFactory extends AsyncConstruct /** * Get fully resolved method list for object, including snake_case and camelCase variants. * - * @param API|MTProto $value Value - * @param string $class Custom class name + * @param API|MTProto|Client $value Value + * @param string $class Custom class name * * @return array */ diff --git a/src/danog/MadelineProto/Db/DbPropertiesFactory.php b/src/danog/MadelineProto/Db/DbPropertiesFactory.php index d12456d5..72815b2c 100644 --- a/src/danog/MadelineProto/Db/DbPropertiesFactory.php +++ b/src/danog/MadelineProto/Db/DbPropertiesFactory.php @@ -27,7 +27,10 @@ class DbPropertiesFactory */ public static function get(DatabaseAbstract $dbSettings, string $namePrefix, string $propertyType, string $name, $value = null): Promise { - $class = __NAMESPACE__; + $propertyType = \strtolower($propertyType); + $class = $propertyType === 'arraynullcache' && !$dbSettings instanceof Memory + ? __NAMESPACE__.'\\NullCache' + : __NAMESPACE__ ; switch (true) { case $dbSettings instanceof Memory: @@ -49,6 +52,7 @@ class DbPropertiesFactory /** @var DbType $class */ switch (\strtolower($propertyType)) { + case 'arraynullcache': case 'array': $class .= 'Array'; break; diff --git a/src/danog/MadelineProto/Db/NullCache/MysqlArray.php b/src/danog/MadelineProto/Db/NullCache/MysqlArray.php new file mode 100644 index 00000000..1fb87d8d --- /dev/null +++ b/src/danog/MadelineProto/Db/NullCache/MysqlArray.php @@ -0,0 +1,10 @@ +API->isIpc(); + } /** * Check whether provided bot API ID is a channel. * diff --git a/src/danog/MadelineProto/Ipc/Client.php b/src/danog/MadelineProto/Ipc/Client.php index 2447fffc..2e52f76b 100644 --- a/src/danog/MadelineProto/Ipc/Client.php +++ b/src/danog/MadelineProto/Ipc/Client.php @@ -159,6 +159,24 @@ class Client $this->run = false; return $this->server->send(Server::SHUTDOWN); } + /** + * Restart IPC server instance. + * + * @internal + */ + public function restartIpcServer(): Promise + { + return $this->server->send(Server::SHUTDOWN); + } + /** + * Whether we're an IPC client instance. + * + * @return boolean + */ + public function isIpc(): bool + { + return true; + } /** * Call function. * diff --git a/src/danog/MadelineProto/Ipc/Server.php b/src/danog/MadelineProto/Ipc/Server.php index d76c5105..4d2fee13 100644 --- a/src/danog/MadelineProto/Ipc/Server.php +++ b/src/danog/MadelineProto/Ipc/Server.php @@ -177,6 +177,7 @@ class Server extends SignalLoop $result = $this->API->{$payload[0]}(...$payload[1]); $result = $result instanceof \Generator ? yield from $result : yield $result; } catch (\Throwable $e) { + $this->API->logger("Got error while calling IPC method: $e", Logger::ERROR); $result = new ExitFailure($e); } try { diff --git a/src/danog/MadelineProto/MTProto.php b/src/danog/MadelineProto/MTProto.php index 2f7e266b..d9af9807 100644 --- a/src/danog/MadelineProto/MTProto.php +++ b/src/danog/MadelineProto/MTProto.php @@ -24,11 +24,13 @@ use Amp\File\StatCache; use Amp\Http\Client\HttpClient; use Amp\Loop; use Amp\Promise; +use Amp\Success; use Closure; use danog\MadelineProto\Async\AsyncConstruct; use danog\MadelineProto\Db\DbArray; use danog\MadelineProto\Db\DbPropertiesFactory; use danog\MadelineProto\Db\DbPropertiesTrait; +use danog\MadelineProto\Db\MemoryArray; use danog\MadelineProto\Ipc\Server; use danog\MadelineProto\Loop\Generic\PeriodicLoopInternal; use danog\MadelineProto\Loop\Update\FeedLoop; @@ -468,6 +470,13 @@ class MTProto extends AsyncConstruct implements TLCallback ] ]; + /** + * Nullcache array for storing main session file to DB. + * + * @var DbArray|Promise[] + */ + public $session; + /** * List of properties stored in database (memory or external). * @see DbPropertiesFactory @@ -478,8 +487,23 @@ class MTProto extends AsyncConstruct implements TLCallback 'full_chats' => 'array', 'channel_participants' => 'array', 'usernames' => 'array', + 'session' => 'arrayNullCache' ]; + /** + * Serialize session, returning object to serialize to db. + * + * @return \Generator + */ + public function serializeSession(object $data): \Generator + { + if ($this->session instanceof MemoryArray) { + return $data; + } + yield $this->session['data'] = \serialize($data); + return $this->session; + } + /** * Constructor function. * @@ -1130,6 +1154,24 @@ class MTProto extends AsyncConstruct implements TLCallback $this->unreference(); $this->logger("Successfully destroyed MadelineProto"); } + /** + * Restart IPC server instance. + * + * @internal + */ + public function restartIpcServer(): Promise + { + return new Success(); // Can only be called from client + } + /** + * Whether we're an IPC client instance. + * + * @return boolean + */ + public function isIpc(): bool + { + return false; + } /** * Parse, update and store settings. * @@ -1166,8 +1208,7 @@ class MTProto extends AsyncConstruct implements TLCallback $this->setupLogger(); if ($reinit) { - $this->__construct(); - yield from $this->initAsynchronously(); + yield from $this->__construct_async($this->settings); } } /** diff --git a/src/danog/MadelineProto/Serialization.php b/src/danog/MadelineProto/Serialization.php index 3e38ecd7..8ed8f6e3 100644 --- a/src/danog/MadelineProto/Serialization.php +++ b/src/danog/MadelineProto/Serialization.php @@ -22,6 +22,8 @@ namespace danog\MadelineProto; use Amp\Deferred; use Amp\Loop; use Amp\Promise; +use danog\MadelineProto\Db\DbArray; +use danog\MadelineProto\Db\DriverArray; use danog\MadelineProto\Ipc\Server; use danog\MadelineProto\MTProtoSession\Session; @@ -191,6 +193,10 @@ abstract class Serialization if ($isNew) { $unserialized = yield from $session->unserialize(); + if ($unserialized instanceof DriverArray) { + yield from $unserialized->initConnection($unserialized->dbSettings); + $unserialized = \unserialize(yield $unserialized['data']); + } } else { $unserialized = yield from self::legacyUnserialize($session->getLegacySessionPath()); } diff --git a/src/danog/MadelineProto/Settings.php b/src/danog/MadelineProto/Settings.php index e489d280..6be169d9 100644 --- a/src/danog/MadelineProto/Settings.php +++ b/src/danog/MadelineProto/Settings.php @@ -177,6 +177,12 @@ class Settings extends SettingsAbstract $this->serialization->merge($settings); } elseif ($settings instanceof TLSchema) { $this->schema->merge($settings); + } elseif ($settings instanceof DatabaseAbstract) { + if (!$this->db instanceof $settings) { + $this->db = $settings; + } else { + $this->db->merge($settings); + } } return; }