From 8f66319b66906a513fd451cbc96d670f7e960aae Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Mon, 10 Jul 2023 21:15:35 +0200 Subject: [PATCH] Implement PSR-4 autoloading for plugin paths --- CHANGELOG.md | 1 + README.md | 7 +- src/EventHandler.php | 71 +++++++++++++------ src/EventHandler/AbstractMessage.php | 46 ++++++++++++ src/EventHandler/Filter/FilterFromSenders.php | 37 ++++++++++ src/InternalDoc.php | 34 +++++++-- src/Ipc/Client.php | 13 ++-- ...HandlerProxy.php => EventHandlerProxy.php} | 2 +- src/Lang.php | 16 ++--- src/LocalFile.php | 16 +++++ src/MTProtoTools/FilesLogic.php | 21 +++--- src/MTProtoTools/MinDatabase.php | 3 +- src/MTProtoTools/UpdateHandler.php | 64 +++++++++++++++-- src/PluginEventHandler.php | 9 ++- src/Settings/Files.php | 2 +- src/SimpleEventHandler.php | 28 ++++++++ src/Wrappers/Events.php | 31 ++++---- 17 files changed, 324 insertions(+), 77 deletions(-) create mode 100644 src/EventHandler/Filter/FilterFromSenders.php rename src/Ipc/{PluginEventHandlerProxy.php => EventHandlerProxy.php} (94%) create mode 100644 src/LocalFile.php create mode 100644 src/SimpleEventHandler.php diff --git a/CHANGELOG.md b/CHANGELOG.md index a6e52b6f5..e9e54d6a6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ Features: - Added support for `pay`, `login_url`, `web_app` and `tg://user?id=` buttons in bot API syntax! - Added a `getAdmin` function that returns the ID of the admin of the bot (which is equal to the first peer returned by getReportPeers in the event handler). - getPlugin can now be used from IPC clients! +- `getReply`, `sendMessage`, `reply` Fixes: - Fixed file uploads with ext-uv! diff --git a/README.md b/README.md index 53c340951..f045b50fa 100644 --- a/README.md +++ b/README.md @@ -458,7 +458,7 @@ Want to add your own open-source project to this list? [Click here!](https://doc * Get diffie-hellman configuration: getDhConfig * Get download info of file: getDownloadInfo * Get download info of the propic of a user: getPropicInfo - * Get event handler: getEventHandler + * Get event handler: getEventHandler * Get extension from file location: getExtensionFromLocation * Get extension from mime type: getExtensionFromMime * Get faved stickers: messages.getFavedStickers @@ -628,7 +628,7 @@ Want to add your own open-source project to this list? [Click here!](https://doc * Notifies the sender about the recipient having listened a voice message or watched a video: messages.readMessageContents * Notify the other user in a private chat that a screenshot of the chat was taken: messages.sendScreenshotNotification * Notify the user that the sent passport data contains some errors The user will not be able to re-submit their Passport data to you until the errors are fixed (the contents of the field for which you returned the error must change): users.setSecureValueErrors - * Obtain a certain event handler plugin instance: getPlugin + * Obtain a certain event handler plugin instance: getPlugin * Obtain a list of bot commands for the specified bot scope and language code: bots.getBotCommands * Obtain a list of related languages that must be used when fetching emoji keyword lists »: messages.getEmojiKeywordsLanguages * Obtain available message reactions »: messages.getAvailableReactions @@ -755,6 +755,7 @@ Want to add your own open-source project to this list? [Click here!](https://doc * Sends a list of messages to all peers (users, chats, channels) of the bot: broadcastMessages * Sends a message to a chat: messages.sendMessage * Sends a message with a file attachment to a secret chat: messages.sendEncryptedFile + * Sends a message: sendMessage * Sends a service message to a secret chat: messages.sendEncryptedService * Sends a text message to a secret chat: messages.sendEncrypted * Sends an updateCustomEvent update to the event handler: sendCustomEvent @@ -827,7 +828,7 @@ Want to add your own open-source project to this list? [Click here!](https://doc * Upload file from callable: uploadFromCallable * Upload file from stream: uploadFromStream * Upload file to secret chat: uploadEncrypted - * Upload file: upload + * Upload file: upload * Upload notification sound, use account.saveRingtone to convert it and add it to the list of saved notification sounds: account.uploadRingtone * Upload theme: account.uploadTheme * Use this method to set the score of the specified user in a game sent as a normal message (bots only): messages.setGameScore diff --git a/src/EventHandler.php b/src/EventHandler.php index ba1ecfa59..14791737c 100644 --- a/src/EventHandler.php +++ b/src/EventHandler.php @@ -155,7 +155,6 @@ abstract class EventHandler extends AbstractAPI $this->setReportPeers(Tools::call($this->getReportPeers())->await()); } - $old = true; $constructors = $this->getTL()->getConstructors(); $methods = []; $handlers = []; @@ -182,6 +181,9 @@ abstract class EventHandler extends AbstractAPI ]; continue; } + if (!$this instanceof SimpleEventHandler) { + continue; + } if ($periodic = $methodRefl->getAttributes(Cron::class)) { $periodic = $periodic[0]->newInstance(); $this->periodicLoops[$method] = new PeriodicLoop( @@ -192,7 +194,6 @@ abstract class EventHandler extends AbstractAPI $periodic->period ); $this->periodicLoops[$method]->start(); - $old = false; continue; } $filter = $methodRefl->getAttributes( @@ -217,9 +218,8 @@ abstract class EventHandler extends AbstractAPI EventLoop::queue($closure, $update); } }; - $old = false; } - if (!$old) { + if ($this instanceof SimpleEventHandler) { self::validateEventHandler(static::class); } if ($has_any) { @@ -236,6 +236,9 @@ abstract class EventHandler extends AbstractAPI $plugin = $pluginsPrev[$class] ?? $pluginsNew[$class] ?? new $class; $pluginsNew[$class] = $plugin; [$newMethods, $newHandlers] = $plugin->internalStart($MadelineProto, $pluginsPrev, $pluginsNew, false) ?? []; + if (!$plugin->isPluginEnabled()) { + continue; + } foreach ($newMethods as $update => $method) { $methods[$update] ??= []; $methods[$update][] = $method; @@ -303,22 +306,47 @@ abstract class EventHandler extends AbstractAPI private static array $includedPaths = []; /** * Obtain a list of plugin event handlers. + * + * @return list> */ private function internalGetPlugins(): array { + $plugins = $this->getPlugins(); + $this->internalGetDirectoryPlugins($plugins); + $plugins = \array_values(\array_unique($plugins, SORT_REGULAR)); + + foreach ($plugins as $plugin) { + Assert::classExists($plugin); + Assert::true(\is_subclass_of($plugin, PluginEventHandler::class), "$plugin must extend ".PluginEventHandler::class); + Assert::notEq($plugin, PluginEventHandler::class); + Assert::true(\str_contains(\ltrim($plugin, '\\'), '\\'), "$plugin must be in a namespace!"); + self::validateEventHandler($plugin); + } + + return $plugins; + } + + private function internalGetDirectoryPlugins(array &$plugins): void + { + if ($this instanceof PluginEventHandler) { + return; + } + $paths = $this->getPluginPaths(); if (\is_string($paths)) { $paths = [$paths]; } elseif ($paths === null) { $paths = []; } + if (!$paths) { + return; + } + $paths = \array_map(realpath(...), $paths); - $plugins = \array_values($this->getPlugins()); - - $recurse = static function (string $path) use (&$recurse, &$plugins): void { + $recurse = static function (string $path, string $namespace = 'MadelinePlugin') use (&$recurse, &$plugins): void { foreach (listFiles($path) as $file) { if (isDirectory($file)) { - $recurse($file); + $recurse($file, $namespace.'\\'.\basename($file)); } elseif (isFile($file) && \str_ends_with($file, "Plugin.php")) { $file = \realpath($file); if (isset(self::$includedPaths[$file])) { @@ -328,8 +356,7 @@ abstract class EventHandler extends AbstractAPI try { require $file; } catch (PluginRegistration $e) { - $name = \substr($e->plugin, \strrpos($e->plugin, '\\')+1); - Assert::eq($name, \basename($file, '.php')); + Assert::eq($e->plugin, $namespace.'\\'.\basename($file, '.php')); $plugins []= $e->plugin; continue; } @@ -345,17 +372,19 @@ abstract class EventHandler extends AbstractAPI self::$includingPlugins = false; } - $plugins = \array_values(\array_unique($plugins, SORT_REGULAR)); - - foreach ($plugins as $plugin) { - Assert::classExists($plugin); - Assert::true(\is_subclass_of($plugin, PluginEventHandler::class), "$plugin must extend ".PluginEventHandler::class); - Assert::notEq($plugin, PluginEventHandler::class); - Assert::true(\str_contains(\ltrim($plugin, '\\'), '\\'), "$plugin must be in a namespace!"); - self::validateEventHandler($plugin); - } - - return $plugins; + \spl_autoload_register(function (string $class) use ($paths): void { + if (!\str_starts_with($class, 'MadelinePlugin\\')) { + return; + } + // Has leading / + $file = \str_replace('\\', DIRECTORY_SEPARATOR, \substr($class, 14)).'.php'; + foreach ($paths as $path) { + if (\file_exists($path.$file)) { + require $path.$file; + Assert::classExists($class); + } + } + }); } private const BANNED_FUNCTIONS = [ diff --git a/src/EventHandler/AbstractMessage.php b/src/EventHandler/AbstractMessage.php index 4210d8b50..a21834e85 100644 --- a/src/EventHandler/AbstractMessage.php +++ b/src/EventHandler/AbstractMessage.php @@ -113,4 +113,50 @@ abstract class AbstractMessage extends Update implements SimpleFilters ] )['messages'][0]); } + + /** + * Replies to the message. + * + * @param string $message Message to send + * @param "html"|"markdown"|null $parseMode Parse mode + * @param array|null $replyMarkup Keyboard information. + * @param integer|null $sendAs Peer to send the message as. + * @param integer|null $scheduleDate Schedule date. + * @param boolean $silent Whether to send the message silently, without triggering notifications. + * @param boolean $background Send this message as background message + * @param boolean $clearDraft Clears the draft field + * @param boolean $noWebpage Set this flag to disable generation of the webpage preview + * @param boolean $updateStickersetsOrder Whether to move used stickersets to top + * + */ + public function reply( + string $message, + ?string $parseMode = null, + ?array $replyMarkup = null, + int|string|null $sendAs = null, + ?int $scheduleDate = null, + bool $silent = false, + bool $noForwards = false, + bool $background = false, + bool $clearDraft = false, + bool $noWebpage = false, + bool $updateStickersetsOrder = false, + ): Message { + return $this->API->sendMessage( + peer: $this->chatId, + message: $message, + parseMode: $parseMode, + replyToMsgId: $this->id, + topMsgId: $this->topicId === 1 ? null : $this->topicId, + replyMarkup: $replyMarkup, + sendAs: $sendAs, + scheduleDate: $scheduleDate, + silent: $silent, + noForwards: $noForwards, + background: $background, + clearDraft: $clearDraft, + noWebpage: $noWebpage, + updateStickersetsOrder: $updateStickersetsOrder + ); + } } diff --git a/src/EventHandler/Filter/FilterFromSenders.php b/src/EventHandler/Filter/FilterFromSenders.php new file mode 100644 index 000000000..22e87394f --- /dev/null +++ b/src/EventHandler/Filter/FilterFromSenders.php @@ -0,0 +1,37 @@ + */ + private readonly array $peers; + /** @var list */ + private readonly array $peersResolved; + public function __construct(string|int ...$idOrUsername) + { + $this->peers = \array_unique($idOrUsername); + } + public function initialize(EventHandler $API): ?Filter + { + $res = []; + foreach ($this->peers as $peer) { + $res []= $API->getId($peer); + } + $this->peersResolved = $res; + return null; + } + public function apply(Update $update): bool + { + return $update instanceof GroupMessage && \in_array($update->senderId, $this->peersResolved, true); + } +} diff --git a/src/InternalDoc.php b/src/InternalDoc.php index adb9436f3..f737f7cac 100644 --- a/src/InternalDoc.php +++ b/src/InternalDoc.php @@ -767,7 +767,7 @@ abstract class InternalDoc /** * Get event handler. */ - public function getEventHandler(): \danog\MadelineProto\EventHandler|\__PHP_Incomplete_Class|null + public function getEventHandler(): \danog\MadelineProto\EventHandler|\danog\MadelineProto\Ipc\EventHandlerProxy|\__PHP_Incomplete_Class|null { return $this->wrapper->getAPI()->getEventHandler(); } @@ -951,7 +951,7 @@ abstract class InternalDoc * * return T|null */ - public function getPlugin(string $class): \danog\MadelineProto\PluginEventHandler|\danog\MadelineProto\Ipc\PluginEventHandlerProxy|null + public function getPlugin(string $class): \danog\MadelineProto\PluginEventHandler|\danog\MadelineProto\Ipc\EventHandlerProxy|null { return $this->wrapper->getAPI()->getPlugin($class); } @@ -1485,6 +1485,28 @@ abstract class InternalDoc { $this->wrapper->getAPI()->sendCustomEvent($payload); } + /** + * Sends a message. + * + * @param integer|string $peer Destination peer or username. + * @param string $message Message to send + * @param "html"|"markdown"|null $parseMode Parse mode + * @param integer|null $replyToMsgId ID of message to reply to. + * @param integer|null $topMsgId ID of thread where to send the message. + * @param array|null $replyMarkup Keyboard information. + * @param integer|null $sendAs Peer to send the message as. + * @param integer|null $scheduleDate Schedule date. + * @param boolean $silent Whether to send the message silently, without triggering notifications. + * @param boolean $background Send this message as background message + * @param boolean $clearDraft Clears the draft field + * @param boolean $noWebpage Set this flag to disable generation of the webpage preview + * @param boolean $updateStickersetsOrder Whether to move used stickersets to top + * + */ + public function sendMessage(string|int $peer, string $message, ?string $parseMode = null, ?int $replyToMsgId = null, ?int $topMsgId = null, ?array $replyMarkup = null, string|int|null $sendAs = null, ?int $scheduleDate = null, bool $silent = false, bool $noForwards = false, bool $background = false, bool $clearDraft = false, bool $noWebpage = false, bool $updateStickersetsOrder = false): \danog\MadelineProto\EventHandler\Message + { + return $this->wrapper->getAPI()->sendMessage($peer, $message, $parseMode, $replyToMsgId, $topMsgId, $replyMarkup, $sendAs, $scheduleDate, $silent, $noForwards, $background, $clearDraft, $noWebpage, $updateStickersetsOrder); + } /** * Set NOOP update handler, ignoring all updates. */ @@ -1740,10 +1762,10 @@ abstract class InternalDoc /** * Upload file. * - * @param FileCallbackInterface|string|array|resource $file File, URL or Telegram file to upload - * @param string $fileName File name - * @param callable $cb Callback (DEPRECATED, use FileCallbackInterface) - * @param boolean $encrypted Whether to encrypt file for secret chats + * @param FileCallbackInterface|LocalFile|string|array|resource $file File, URL or Telegram file to upload + * @param string $fileName File name + * @param callable $cb Callback (DEPRECATED, use FileCallbackInterface) + * @param boolean $encrypted Whether to encrypt file for secret chats */ public function upload($file, string $fileName = '', ?callable $cb = null, bool $encrypted = false) { diff --git a/src/Ipc/Client.php b/src/Ipc/Client.php index bafc68143..9c127b249 100644 --- a/src/Ipc/Client.php +++ b/src/Ipc/Client.php @@ -312,17 +312,12 @@ final class Client extends ClientAbstract { throw new Exception("Can't use ".__FUNCTION__.' in an IPC client instance, please use startAndLoop, instead!'); } - /** - * Placeholder. - * - * @param mixed ...$params Params - */ - public function getEventHandler(mixed ...$params): void + public function getEventHandler(): EventHandlerProxy { - throw new Exception("Can't use ".__FUNCTION__.' in an IPC client instance, please use startAndLoop, instead!'); + return $this->hasEventHandler() ? new EventHandlerProxy(null, $this) : null; } - public function getPlugin(string $class): ?PluginEventHandlerProxy + public function getPlugin(string $class): ?EventHandlerProxy { - return $this->hasPlugin($class) ? new PluginEventHandlerProxy($class, $this) : null; + return $this->hasPlugin($class) ? new EventHandlerProxy($class, $this) : null; } } diff --git a/src/Ipc/PluginEventHandlerProxy.php b/src/Ipc/EventHandlerProxy.php similarity index 94% rename from src/Ipc/PluginEventHandlerProxy.php rename to src/Ipc/EventHandlerProxy.php index c7f982e7a..19068fb78 100644 --- a/src/Ipc/PluginEventHandlerProxy.php +++ b/src/Ipc/EventHandlerProxy.php @@ -5,7 +5,7 @@ namespace danog\MadelineProto\Ipc; /** * Plugin event handler proxy object, for use through the IPC API. */ -final class PluginEventHandlerProxy extends IpcCapable +final class EventHandlerProxy extends IpcCapable { public function __construct( private readonly ?string $__plugin, diff --git a/src/Lang.php b/src/Lang.php index 04f90e2fa..adbabc029 100644 --- a/src/Lang.php +++ b/src/Lang.php @@ -118,7 +118,7 @@ final class Lang 'madelineproto_ready' => 'MadelineProto ئامادەیە!', 'manualAdminActionRequired' => '!!!!!!!!! MANUAL SYSTEM ADMIN ACTION REQUIRED !!!!!!!!!', 'method_not_found' => 'نەتوانرا شێواز بدۆزرێتەوە: ', - 'mmapErrorPart1' => 'The maximum number of mmap\'ed regions was reached (%s): please increase the vm.max_map_count kernel config to 262144 to fix.', + 'mmapErrorPart1' => 'The maximum number of memory mapped (mmap) regions was reached (%s): please increase the vm.max_map_count kernel config to 262144 to fix.', 'mmapErrorPart2' => 'To fix, run the following command as root: %s', 'mmapErrorPart3' => 'To persist the change across reboots: %s', 'mmapErrorPart4' => 'On Windows and WSL, increasing the size of the pagefile might help; please switch to native Linux if the issue persists.', @@ -251,7 +251,7 @@ final class Lang 'madelineproto_ready' => 'MadelineProto is ready!', 'manualAdminActionRequired' => '!!!!!!!!! MANUAL SYSTEM ADMIN ACTION REQUIRED !!!!!!!!!', 'method_not_found' => 'Could not find method: ', - 'mmapErrorPart1' => 'The maximum number of mmap\'ed regions was reached (%s): please increase the vm.max_map_count kernel config to 262144 to fix.', + 'mmapErrorPart1' => 'The maximum number of memory mapped (mmap) regions was reached (%s): please increase the vm.max_map_count kernel config to 262144 to fix.', 'mmapErrorPart2' => 'To fix, run the following command as root: %s', 'mmapErrorPart3' => 'To persist the change across reboots: %s', 'mmapErrorPart4' => 'On Windows and WSL, increasing the size of the pagefile might help; please switch to native Linux if the issue persists.', @@ -384,7 +384,7 @@ final class Lang 'madelineproto_ready' => 'MadelineProto آماده است!', 'manualAdminActionRequired' => '!!!!!!!!! MANUAL SYSTEM ADMIN ACTION REQUIRED !!!!!!!!!', 'method_not_found' => 'تابع پیدا نشد: ', - 'mmapErrorPart1' => 'The maximum number of mmap\'ed regions was reached (%s): please increase the vm.max_map_count kernel config to 262144 to fix.', + 'mmapErrorPart1' => 'The maximum number of memory mapped (mmap) regions was reached (%s): please increase the vm.max_map_count kernel config to 262144 to fix.', 'mmapErrorPart2' => 'To fix, run the following command as root: %s', 'mmapErrorPart3' => 'To persist the change across reboots: %s', 'mmapErrorPart4' => 'On Windows and WSL, increasing the size of the pagefile might help; please switch to native Linux if the issue persists.', @@ -517,7 +517,7 @@ final class Lang 'madelineproto_ready' => 'MadelineProto est prêt!', 'manualAdminActionRequired' => '!!!!!!!!! MANUAL SYSTEM ADMIN ACTION REQUIRED !!!!!!!!!', 'method_not_found' => 'Could not find method: ', - 'mmapErrorPart1' => 'The maximum number of mmap\'ed regions was reached (%s): please increase the vm.max_map_count kernel config to 262144 to fix.', + 'mmapErrorPart1' => 'The maximum number of memory mapped (mmap) regions was reached (%s): please increase the vm.max_map_count kernel config to 262144 to fix.', 'mmapErrorPart2' => 'To fix, run the following command as root: %s', 'mmapErrorPart3' => 'To persist the change across reboots: %s', 'mmapErrorPart4' => 'On Windows and WSL, increasing the size of the pagefile might help; please switch to native Linux if the issue persists.', @@ -650,7 +650,7 @@ final class Lang 'madelineproto_ready' => 'MadelineProto מוכן!', 'manualAdminActionRequired' => '!!!!!!!!! MANUAL SYSTEM ADMIN ACTION REQUIRED !!!!!!!!!', 'method_not_found' => 'הפונקציה לא נמצא: ', - 'mmapErrorPart1' => 'The maximum number of mmap\'ed regions was reached (%s): please increase the vm.max_map_count kernel config to 262144 to fix.', + 'mmapErrorPart1' => 'The maximum number of memory mapped (mmap) regions was reached (%s): please increase the vm.max_map_count kernel config to 262144 to fix.', 'mmapErrorPart2' => 'To fix, run the following command as root: %s', 'mmapErrorPart3' => 'To persist the change across reboots: %s', 'mmapErrorPart4' => 'On Windows and WSL, increasing the size of the pagefile might help; please switch to native Linux if the issue persists.', @@ -916,7 +916,7 @@ final class Lang 'madelineproto_ready' => 'MadelineProto is ready!', 'manualAdminActionRequired' => '!!!!!!!!! MANUAL SYSTEM ADMIN ACTION REQUIRED !!!!!!!!!', 'method_not_found' => 'Could not find method: ', - 'mmapErrorPart1' => 'The maximum number of mmap\'ed regions was reached (%s): please increase the vm.max_map_count kernel config to 262144 to fix.', + 'mmapErrorPart1' => 'The maximum number of memory mapped (mmap) regions was reached (%s): please increase the vm.max_map_count kernel config to 262144 to fix.', 'mmapErrorPart2' => 'To fix, run the following command as root: %s', 'mmapErrorPart3' => 'To persist the change across reboots: %s', 'mmapErrorPart4' => 'On Windows and WSL, increasing the size of the pagefile might help; please switch to native Linux if the issue persists.', @@ -1049,7 +1049,7 @@ final class Lang 'madelineproto_ready' => 'MadelineProto tayyor!', 'manualAdminActionRequired' => '!!!!!!!!! MANUAL SYSTEM ADMIN ACTION REQUIRED !!!!!!!!!', 'method_not_found' => 'Metodni topib bo\'lmadi: ', - 'mmapErrorPart1' => 'The maximum number of mmap\'ed regions was reached (%s): please increase the vm.max_map_count kernel config to 262144 to fix.', + 'mmapErrorPart1' => 'The maximum number of memory mapped (mmap) regions was reached (%s): please increase the vm.max_map_count kernel config to 262144 to fix.', 'mmapErrorPart2' => 'To fix, run the following command as root: %s', 'mmapErrorPart3' => 'To persist the change across reboots: %s', 'mmapErrorPart4' => 'On Windows and WSL, increasing the size of the pagefile might help; please switch to native Linux if the issue persists.', @@ -1184,7 +1184,7 @@ final class Lang 'madelineproto_ready' => 'MadelineProto is ready!', 'manualAdminActionRequired' => '!!!!!!!!! MANUAL SYSTEM ADMIN ACTION REQUIRED !!!!!!!!!', 'method_not_found' => 'Could not find method: ', - 'mmapErrorPart1' => 'The maximum number of mmap\'ed regions was reached (%s): please increase the vm.max_map_count kernel config to 262144 to fix.', + 'mmapErrorPart1' => 'The maximum number of memory mapped (mmap) regions was reached (%s): please increase the vm.max_map_count kernel config to 262144 to fix.', 'mmapErrorPart2' => 'To fix, run the following command as root: %s', 'mmapErrorPart3' => 'To persist the change across reboots: %s', 'mmapErrorPart4' => 'On Windows and WSL, increasing the size of the pagefile might help; please switch to native Linux if the issue persists.', diff --git a/src/LocalFile.php b/src/LocalFile.php new file mode 100644 index 000000000..de3fdd693 --- /dev/null +++ b/src/LocalFile.php @@ -0,0 +1,16 @@ +uploadFromStream($file, 0, '', $fileName, $cb, $encrypted); } - $settings = $this->getSettings(); - /** @var Settings $settings */ - if (!$settings->getFiles()->getAllowAutomaticUpload()) { - return $this->uploadFromUrl($file, 0, $fileName, $cb, $encrypted); + if ($file instanceof LocalFile) { + $file = $file->file; + } else { + /** @var Settings $settings */ + $settings = $this->getSettings(); + if (!$settings->getFiles()->getAllowAutomaticUpload()) { + return $this->uploadFromUrl($file, 0, $fileName, $cb, $encrypted); + } } $file = Tools::absolute($file); if (!exists($file)) { diff --git a/src/MTProtoTools/MinDatabase.php b/src/MTProtoTools/MinDatabase.php index 71905aa4b..62c2e9db7 100644 --- a/src/MTProtoTools/MinDatabase.php +++ b/src/MTProtoTools/MinDatabase.php @@ -101,7 +101,8 @@ final class MinDatabase implements TLCallback }); } } - public function sync(): void { + public function sync(): void + { if (!$this->synced) { EventLoop::queue(function (): void { $counter = 0; diff --git a/src/MTProtoTools/UpdateHandler.php b/src/MTProtoTools/UpdateHandler.php index 2907c3c47..db8c930c7 100644 --- a/src/MTProtoTools/UpdateHandler.php +++ b/src/MTProtoTools/UpdateHandler.php @@ -31,11 +31,11 @@ use danog\MadelineProto\EventHandler\Message; use danog\MadelineProto\EventHandler\Message\ChannelMessage; use danog\MadelineProto\EventHandler\Message\GroupMessage; use danog\MadelineProto\EventHandler\Message\PrivateMessage; -use danog\MadelineProto\EventHandler\Service\DialogCreated; -use danog\MadelineProto\EventHandler\Service\DialogMemberLeft; -use danog\MadelineProto\EventHandler\Service\DialogMembersJoined; -use danog\MadelineProto\EventHandler\Service\DialogPhotoChanged; -use danog\MadelineProto\EventHandler\Service\DialogTitleChanged; +use danog\MadelineProto\EventHandler\Message\Service\DialogCreated; +use danog\MadelineProto\EventHandler\Message\Service\DialogMemberLeft; +use danog\MadelineProto\EventHandler\Message\Service\DialogMembersJoined; +use danog\MadelineProto\EventHandler\Message\Service\DialogPhotoChanged; +use danog\MadelineProto\EventHandler\Message\Service\DialogTitleChanged; use danog\MadelineProto\EventHandler\Update; use danog\MadelineProto\Exception; use danog\MadelineProto\Lang; @@ -391,6 +391,60 @@ trait UpdateHandler API::PEER_TYPE_CHANNEL => new ChannelMessage($this, $message), }; } + /** + * Sends a message. + * + * @param integer|string $peer Destination peer or username. + * @param string $message Message to send + * @param "html"|"markdown"|null $parseMode Parse mode + * @param integer|null $replyToMsgId ID of message to reply to. + * @param integer|null $topMsgId ID of thread where to send the message. + * @param array|null $replyMarkup Keyboard information. + * @param integer|null $sendAs Peer to send the message as. + * @param integer|null $scheduleDate Schedule date. + * @param boolean $silent Whether to send the message silently, without triggering notifications. + * @param boolean $background Send this message as background message + * @param boolean $clearDraft Clears the draft field + * @param boolean $noWebpage Set this flag to disable generation of the webpage preview + * @param boolean $updateStickersetsOrder Whether to move used stickersets to top + * + */ + public function sendMessage( + int|string $peer, + string $message, + ?string $parseMode = null, + ?int $replyToMsgId = null, + ?int $topMsgId = null, + ?array $replyMarkup = null, + int|string|null $sendAs = null, + ?int $scheduleDate = null, + bool $silent = false, + bool $noForwards = false, + bool $background = false, + bool $clearDraft = false, + bool $noWebpage = false, + bool $updateStickersetsOrder = false, + ): Message { + return $this->wrapMessage($this->extractMessage($this->methodCallAsyncRead( + 'messages.sendMessage', + [ + 'peer' => $peer, + 'message' => $message, + 'parse_mode' => $parseMode, + 'reply_to_msg_id' => $replyToMsgId, + 'top_msg_id' => $topMsgId, + 'reply_markup' => $replyMarkup, + 'send_as' => $sendAs, + 'schedule_date' => $scheduleDate, + 'silent' => $silent, + 'noforwards' => $noForwards, + 'background' => $background, + 'clear_draft' => $clearDraft, + 'no_webpage' => $noWebpage, + 'update_stickersets_order' => $updateStickersetsOrder + ] + ))); + } /** * Extract a message ID from an Updates constructor. */ diff --git a/src/PluginEventHandler.php b/src/PluginEventHandler.php index 933db8c45..2b27d368c 100644 --- a/src/PluginEventHandler.php +++ b/src/PluginEventHandler.php @@ -23,7 +23,7 @@ namespace danog\MadelineProto; /** * Plugin event handler class. */ -abstract class PluginEventHandler extends EventHandler +abstract class PluginEventHandler extends SimpleEventHandler { /** * Plugins can require other plugins ONLY with the getPlugins() method. @@ -32,4 +32,11 @@ abstract class PluginEventHandler extends EventHandler { return null; } + /** + * Whether the plugin is enabled. + */ + public function isPluginEnabled(): bool + { + return true; + } } diff --git a/src/Settings/Files.php b/src/Settings/Files.php index 2047c4cd5..2403ff292 100644 --- a/src/Settings/Files.php +++ b/src/Settings/Files.php @@ -14,7 +14,7 @@ final class Files extends SettingsAbstract /** * Allow automatic upload of files from file paths present in constructors? */ - protected bool $allowAutomaticUpload = true; + protected bool $allowAutomaticUpload = false; /** * Upload parallel chunk count. */ diff --git a/src/SimpleEventHandler.php b/src/SimpleEventHandler.php new file mode 100644 index 000000000..326b694e2 --- /dev/null +++ b/src/SimpleEventHandler.php @@ -0,0 +1,28 @@ +. + * + * @author Daniil Gentili + * @copyright 2016-2023 Daniil Gentili + * @license https://opensource.org/licenses/AGPL-3.0 AGPLv3 + * @link https://docs.madelineproto.xyz MadelineProto documentation + */ + +namespace danog\MadelineProto; + +/** + * Simple event handler class: by extending this class, you can use filters, crons and the simplified event handler API. + */ +abstract class SimpleEventHandler extends EventHandler +{ +} diff --git a/src/Wrappers/Events.php b/src/Wrappers/Events.php index ab161cc36..d8d78c307 100644 --- a/src/Wrappers/Events.php +++ b/src/Wrappers/Events.php @@ -23,7 +23,7 @@ namespace danog\MadelineProto\Wrappers; use __PHP_Incomplete_Class; use danog\MadelineProto\EventHandler; use danog\MadelineProto\Exception; -use danog\MadelineProto\Ipc\PluginEventHandlerProxy; +use danog\MadelineProto\Ipc\EventHandlerProxy; use danog\MadelineProto\PluginEventHandler; use danog\MadelineProto\Settings; use danog\MadelineProto\UpdateHandlerType; @@ -121,7 +121,7 @@ trait Events * * return T|null */ - final public function getPlugin(string $class): PluginEventHandler|PluginEventHandlerProxy|null + final public function getPlugin(string $class): PluginEventHandler|EventHandlerProxy|null { return $this->pluginInstances[$class] ?? null; } @@ -141,7 +141,7 @@ trait Events /** * Get event handler. */ - public function getEventHandler(): EventHandler|__PHP_Incomplete_Class|null + public function getEventHandler(): EventHandler|EventHandlerProxy|__PHP_Incomplete_Class|null { return $this->event_handler_instance; } @@ -153,28 +153,33 @@ trait Events return isset($this->event_handler_instance); } /** @internal */ - public function callPluginMethod(string $class, string $method, array $args): mixed + public function callPluginMethod(?string $class, string $method, array $args): mixed { - return $this->pluginInstances[$class]->$method(...$args); + $obj = $class === null ? $this->event_handler_instance : $this->pluginInstances[$class]; + return $obj->$method(...$args); } /** @internal */ - public function setPluginProperty(string $class, string $property, mixed $value): void + public function setPluginProperty(?string $class, string $property, mixed $value): void { - $this->pluginInstances[$class]->$property = $value; + $obj = $class === null ? $this->event_handler_instance : $this->pluginInstances[$class]; + $obj->$property = $value; } /** @internal */ - public function getPluginProperty(string $class, string $property): mixed + public function getPluginProperty(?string $class, string $property): mixed { - return $this->pluginInstances[$class]->$property; + $obj = $class === null ? $this->event_handler_instance : $this->pluginInstances[$class]; + return $obj->$property; } /** @internal */ - public function issetPluginProperty(string $class, string $property): bool + public function issetPluginProperty(?string $class, string $property): bool { - return isset($this->pluginInstances[$class]->$property); + $obj = $class === null ? $this->event_handler_instance : $this->pluginInstances[$class]; + return isset($obj->$property); } /** @internal */ - public function unsetPluginProperty(string $class, string $property): void + public function unsetPluginProperty(?string $class, string $property): void { - unset($this->pluginInstances[$class]->$property); + $obj = $class === null ? $this->event_handler_instance : $this->pluginInstances[$class]; + unset($obj->$property); } }