1
0
mirror of https://github.com/danog/MadelineProto.git synced 2024-11-26 21:34:41 +01:00

Implement PSR-4 autoloading for plugin paths

This commit is contained in:
Daniil Gentili 2023-07-10 21:15:35 +02:00
parent 7d47e1f126
commit 8f66319b66
Signed by: danog
GPG Key ID: 8C1BE3B34B230CA7
17 changed files with 324 additions and 77 deletions

View File

@ -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!

View File

@ -458,7 +458,7 @@ Want to add your own open-source project to this list? [Click here!](https://doc
* <a href="https://docs.madelineproto.xyz/PHP/danog/MadelineProto/API.html#getdhconfig-array" name="getDhConfig">Get diffie-hellman configuration: getDhConfig</a>
* <a href="https://docs.madelineproto.xyz/PHP/danog/MadelineProto/API.html#getdownloadinfo-mixed-messagemedia-array-ext-string-name-string-mime-string-size-int-inputfilelocation-array" name="getDownloadInfo">Get download info of file: getDownloadInfo</a>
* <a href="https://docs.madelineproto.xyz/PHP/danog/MadelineProto/API.html#getpropicinfo-mixed-data-array" name="getPropicInfo">Get download info of the propic of a user: getPropicInfo</a>
* <a href="https://docs.madelineproto.xyz/PHP/danog/MadelineProto/API.html#geteventhandler-danog-madelineproto-eventhandler-__php_incomplete_class-null" name="getEventHandler">Get event handler: getEventHandler</a>
* <a href="https://docs.madelineproto.xyz/PHP/danog/MadelineProto/API.html#geteventhandler-danog-madelineproto-eventhandler-danog-madelineproto-ipc-eventhandlerproxy-__php_incomplete_class-null" name="getEventHandler">Get event handler: getEventHandler</a>
* <a href="https://docs.madelineproto.xyz/PHP/danog/MadelineProto/API.html#getextensionfromlocation-mixed-location-string-default-string" name="getExtensionFromLocation">Get extension from file location: getExtensionFromLocation</a>
* <a href="https://docs.madelineproto.xyz/PHP/danog/MadelineProto/API.html#getextensionfrommime-string-mime-string" name="getExtensionFromMime">Get extension from mime type: getExtensionFromMime</a>
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages.getFavedStickers.html" name="messages.getFavedStickers">Get faved stickers: messages.getFavedStickers</a>
@ -628,7 +628,7 @@ Want to add your own open-source project to this list? [Click here!](https://doc
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages.readMessageContents.html" name="messages.readMessageContents">Notifies the sender about the recipient having listened a voice message or watched a video: messages.readMessageContents</a>
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages.sendScreenshotNotification.html" name="messages.sendScreenshotNotification">Notify the other user in a private chat that a screenshot of the chat was taken: messages.sendScreenshotNotification</a>
* <a href="https://docs.madelineproto.xyz/API_docs/methods/users.setSecureValueErrors.html" name="users.setSecureValueErrors">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</a>
* <a href="https://docs.madelineproto.xyz/PHP/danog/MadelineProto/API.html#getplugin-class-string-t-class-danog-madelineproto-plugineventhandler-danog-madelineproto-ipc-plugineventhandlerproxy-null" name="getPlugin">Obtain a certain event handler plugin instance: getPlugin</a>
* <a href="https://docs.madelineproto.xyz/PHP/danog/MadelineProto/API.html#getplugin-class-string-t-class-danog-madelineproto-plugineventhandler-danog-madelineproto-ipc-eventhandlerproxy-null" name="getPlugin">Obtain a certain event handler plugin instance: getPlugin</a>
* <a href="https://docs.madelineproto.xyz/API_docs/methods/bots.getBotCommands.html" name="bots.getBotCommands">Obtain a list of bot commands for the specified bot scope and language code: bots.getBotCommands</a>
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages.getEmojiKeywordsLanguages.html" name="messages.getEmojiKeywordsLanguages">Obtain a list of related languages that must be used when fetching emoji keyword lists »: messages.getEmojiKeywordsLanguages</a>
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages.getAvailableReactions.html" name="messages.getAvailableReactions">Obtain available message reactions »: messages.getAvailableReactions</a>
@ -755,6 +755,7 @@ Want to add your own open-source project to this list? [Click here!](https://doc
* <a href="https://docs.madelineproto.xyz/PHP/danog/MadelineProto/API.html#broadcastmessages-array-messages-danog-madelineproto-broadcast-filter-filter-bool-pin-int" name="broadcastMessages">Sends a list of messages to all peers (users, chats, channels) of the bot: broadcastMessages</a>
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages.sendMessage.html" name="messages.sendMessage">Sends a message to a chat: messages.sendMessage</a>
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages.sendEncryptedFile.html" name="messages.sendEncryptedFile">Sends a message with a file attachment to a secret chat: messages.sendEncryptedFile</a>
* <a href="https://docs.madelineproto.xyz/PHP/danog/MadelineProto/API.html#sendmessage-int-string-peer-string-message-html-markdown-null-parsemode-int-null-replytomsgid-int-null-topmsgid-array-null-replymarkup-int-null-sendas-int-null-scheduledate-bool-silent-bool-noforwards-bool-background-bool-cleardraft-bool-nowebpage-bool-updatestickersetsorder-message" name="sendMessage">Sends a message: sendMessage</a>
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages.sendEncryptedService.html" name="messages.sendEncryptedService">Sends a service message to a secret chat: messages.sendEncryptedService</a>
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages.sendEncrypted.html" name="messages.sendEncrypted">Sends a text message to a secret chat: messages.sendEncrypted</a>
* <a href="https://docs.madelineproto.xyz/PHP/danog/MadelineProto/API.html#sendcustomevent-mixed-payload-void" name="sendCustomEvent">Sends an updateCustomEvent update to the event handler: sendCustomEvent</a>
@ -827,7 +828,7 @@ Want to add your own open-source project to this list? [Click here!](https://doc
* <a href="https://docs.madelineproto.xyz/PHP/danog/MadelineProto/API.html#uploadfromcallable-mixed-callable-int-size-string-mime-string-filename-callable-cb-bool-seekable-bool-encrypted-mixed" name="uploadFromCallable">Upload file from callable: uploadFromCallable</a>
* <a href="https://docs.madelineproto.xyz/PHP/danog/MadelineProto/API.html#uploadfromstream-mixed-stream-int-size-string-mime-string-filename-callable-cb-bool-encrypted-mixed" name="uploadFromStream">Upload file from stream: uploadFromStream</a>
* <a href="https://docs.madelineproto.xyz/PHP/danog/MadelineProto/API.html#uploadencrypted-filecallbackinterface-string-array-file-string-filename-callable-cb-mixed" name="uploadEncrypted">Upload file to secret chat: uploadEncrypted</a>
* <a href="https://docs.madelineproto.xyz/PHP/danog/MadelineProto/API.html#upload-filecallbackinterface-string-array-resource-file-string-filename-callable-cb-bool-encrypted-mixed" name="upload">Upload file: upload</a>
* <a href="https://docs.madelineproto.xyz/PHP/danog/MadelineProto/API.html#upload-filecallbackinterface-localfile-string-array-resource-file-string-filename-callable-cb-bool-encrypted-mixed" name="upload">Upload file: upload</a>
* <a href="https://docs.madelineproto.xyz/API_docs/methods/account.uploadRingtone.html" name="account.uploadRingtone">Upload notification sound, use account.saveRingtone to convert it and add it to the list of saved notification sounds: account.uploadRingtone</a>
* <a href="https://docs.madelineproto.xyz/API_docs/methods/account.uploadTheme.html" name="account.uploadTheme">Upload theme: account.uploadTheme</a>
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages.setGameScore.html" name="messages.setGameScore">Use this method to set the score of the specified user in a game sent as a normal message (bots only): messages.setGameScore</a>

View File

@ -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<class-string<PluginEventHandler>>
*/
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 = [

View File

@ -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
);
}
}

View File

@ -0,0 +1,37 @@
<?php declare(strict_types=1);
namespace danog\MadelineProto\EventHandler\Filter;
use Attribute;
use danog\MadelineProto\EventHandler;
use danog\MadelineProto\EventHandler\Message\GroupMessage;
use danog\MadelineProto\EventHandler\Update;
/**
* Allow incoming or outgoing group messages made by a certain list of senders.
*/
#[Attribute(Attribute::TARGET_METHOD)]
final class FilterFromSenders extends Filter
{
/** @var array<string|int> */
private readonly array $peers;
/** @var list<string|int> */
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);
}
}

View File

@ -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)
{

View File

@ -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;
}
}

View File

@ -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,

View File

@ -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.',

16
src/LocalFile.php Normal file
View File

@ -0,0 +1,16 @@
<?php
declare(strict_types=1);
namespace danog\MadelineProto;
/**
* Indicates a local file to upload.
*/
final class LocalFile
{
public function __construct(
public readonly string $file
) {
}
}

View File

@ -21,6 +21,7 @@ use Amp\Sync\Lock;
use danog\MadelineProto\Exception;
use danog\MadelineProto\FileCallbackInterface;
use danog\MadelineProto\Lang;
use danog\MadelineProto\LocalFile;
use danog\MadelineProto\NothingInTheSocketException;
use danog\MadelineProto\Settings;
use danog\MadelineProto\Stream\Common\BufferedRawStream;
@ -229,10 +230,10 @@ trait FilesLogic
/**
* 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)
{
@ -250,10 +251,14 @@ trait FilesLogic
if (\is_resource($file) || (\is_object($file) && $file instanceof ReadableStream)) {
return $this->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)) {

View File

@ -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;

View File

@ -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.
*/

View File

@ -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;
}
}

View File

@ -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.
*/

View File

@ -0,0 +1,28 @@
<?php
declare(strict_types=1);
/**
* EventHandler module.
*
* This file is part of MadelineProto.
* MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
* MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Affero General Public License for more details.
* You should have received a copy of the GNU General Public License along with MadelineProto.
* If not, see <http://www.gnu.org/licenses/>.
*
* @author Daniil Gentili <daniil@daniil.it>
* @copyright 2016-2023 Daniil Gentili <daniil@daniil.it>
* @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
{
}

View File

@ -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);
}
}