diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8a52b3720..3b0c96721 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,22 +1,92 @@
-MadelineProto was updated (8.0.0-beta100)!
+Introducing MadelineProto's biggest update yet, 8.0.0-beta100!
-Features:
+This version introduces [plugins](https://docs.madelineproto.xyz/docs/PLUGINS.html), [bound methods](https://docs.madelineproto.xyz/docs/UPDATES.html#bound-methods), [filters](https://docs.madelineproto.xyz/docs/FILTERS.html), [a built-in cron system](https://docs.madelineproto.xyz/docs/UPDATES.html#cron), [IPC support for the event handler](https://docs.madelineproto.xyz/docs/UPDATES.html#persisting-data-and-ipc) and automatic static analysis for event handler code.
+
+To create a plugin, simply create an event handler that extends PluginEventHandler.
+
+For example, create a `plugins/Danogentili/PingPlugin.php` file:
+```
+commandArgs;
+
+ $message->reply($args[0] ?? '');
+ }
+
+ #[FilterRegex('/.*(mt?proto).*/i')]
+ public function testRegex(Incoming & Message $message): void
+ {
+ $message->reply("Did you mean to write MadelineProto instead of ".$message->matches[1].'?');
+ }
+
+ #[FilterText('ping')]
+ public function pingCommand(Incoming&Message $message): void
+ {
+ $message->reply("Pong");
+ }
+}
+```
+
+And use a [plugin base](https://raw.githubusercontent.com/danog/MadelineProto/v8/examples/PluginBase.php) to run all plugins included in the `plugins` folder.
+
+See the [documentation](https://docs.madelineproto.xyz/docs/PLUGINS.html) for more info on how to create MadelineProto plugins!
+
+Both plugins and normal bots can make use of [bound update methods](https://docs.madelineproto.xyz/docs/UPDATES.html#bound-methods) like `reply()`, `delete()`, `getReply()`, `getHTML()` and simplified properties like `chatId`, `senderId`, `command`, `commandArgs` and many more, see the [documentation](https://docs.madelineproto.xyz/docs/UPDATES.html#bound-methods) for more info!
+
+Plugins and bots can now use three different filtering systems, to easily receive only updates satisfying certain conditions (incoming/outgoing, from group, channel, private, from an admin or a specific peer, with an audio/sticker/..., satisfying a certain regex or a certain /command, and much more!), [see the documentation](https://docs.madelineproto.xyz/docs/FILTERS.html) for more info!
+
+All event handler methods marked by the `Cron` attribute are now automatically invoked by MadelineProto every `period` seconds:
+
+```
+use danog\MadelineProto\EventHandler\Attributes\Cron;
+
+class MyEventHandler extends SimpleEventHandler
+{
+ /**
+ * This cron function will be executed forever, every 60 seconds.
+ */
+ #[Cron(period: 60.0)]
+ public function cron1(): void
+ {
+ $this->sendMessageToAdmins("The bot is online, current time ".date(DATE_RFC850)."!");
+ }
+}
+```
+
+See the [documentation](https://docs.madelineproto.xyz/docs/UPDATES.html#cron) for more info!
+
+Finally, all new bots and plugins will be automatically analyzed by MadelineProto, blocking execution if performance or security issues are detected!
+
+Other features:
- Thanks to the many translation contributors @ https://weblate.madelineproto.xyz/, MadelineProto is now localized in Hebrew, Persian, Kurdish, Uzbek, Russian, French and Italian!
- You can now use `Tools::callFork` to fork a new green thread!
- You can now automatically pin messages broadcasted using `broadcastMessages`, `broadcastForwardMessages` by using the new `pin: true` parameter!
- You can now use `sendMessageToAdmins` to send messages to the bot's admin (the peers returned by `getReportPeers`).
-- Added `wrapUpdate`, `wrapMessage`, `wrapMedia`
-- Added `Cron`
-- Added plugins, filters, simple filters
+- Added `wrapUpdate`, `wrapMessage`, `wrapMedia` methods to wrap low-level MTProto updates into an abstracted Message object with bound methods!
- The `waveform` attribute of `Voice` objects is now automatically encoded and decoded to an array of 100 integer values!
-- Added a custom PeerNotInDbException class for "This peer is not present in the internal peer database" errors
+- Added a custom `PeerNotInDbException` class for "This peer is not present in the internal peer database" errors
- Added a `label` property to the Button class, directly indicating the button label (instead of manually fetching it as an array key).
- Added `isForum` method to check whether a given supergroup is a forum
-- Added `entitiesToHtml` method to convert a message and a set of Telegram entities to an HTML string!
+- Added an `entitiesToHtml` method to convert a message and a set of Telegram entities to an HTML string!
- You can now use `reportMemoryProfile()` to generate and send a `pprof` memory profile to all report peers to debug the causes of high memory usage.
- 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!
+- Added a `getAdminIds` function that returns the IDs of the admin of the bot (equal to the peers returned by getReportPeers in the event handler).
+- getEventHandler can now be used from IPC clients!
+- Added `Cron`
+- Added plugins, filters, simple filters
- `getReply`, `sendMessage`, `sendDocument`, `sendPhoto`, `reply`, `delete`
Fixes:
@@ -26,8 +96,8 @@ Fixes:
- Fixed a bug that caused updates to get paused if an exception is thrown during onStart.
- Broadcast IDs are now unique across multiple broadcasts, even if previous broadcasts already completed their ID will never be re-used.
- Now uploadMedia, sendMedia and upload can upload files from string buffers created using `ReadableBuffer`.
-- Reduce memory usage during flood waits by tweaking config defaults.
-- Reduce memory usage by clearing the min database automatically as needed.
-- Automatically try caching all dialogs if a peer not found error is about to be thrown
-- Fix some issues with pure phar installs
-- And many performance improvements and bugfixes!
+- Reduced memory usage during flood waits by tweaking config defaults.
+- Reduced memory usage by clearing the min database automatically as needed.
+- Automatically try caching all dialogs if a peer not found error is about to be thrown.
+- Fixed some issues with pure phar installs.
+- And many other performance improvements and bugfixes!
diff --git a/README.md b/README.md
index 28ecfdc3c..661b4e5c8 100644
--- a/README.md
+++ b/README.md
@@ -90,11 +90,24 @@ Want to add your own open-source project to this list? [Click here!](https://doc
* [Broadcasting messages to all users](https://docs.madelineproto.xyz/docs/BROADCAST.html)
* [Handling updates (new messages & other events)](https://docs.madelineproto.xyz/docs/UPDATES.html)
* [Async Event driven](https://docs.madelineproto.xyz/docs/UPDATES.html#async-event-driven)
+ * [Full example](https://docs.madelineproto.xyz/docs/UPDATES.html#async-event-driven)
+ * [Bound methods](https://docs.madelineproto.xyz/docs/UPDATES.html#bound-methods)
+ * [Filters](https://docs.madelineproto.xyz/docs/FILTERS.html)
+ * [Simple filters](https://docs.madelineproto.xyz/docs/FILTERS.html#simple-filters)
+ * [Attribute filters](https://docs.madelineproto.xyz/docs/FILTERS.html#attribute-filters)
+ * [MTProto filters](https://docs.madelineproto.xyz/docs/FILTERS.html#mtproto-filters)
+ * [Plugins](https://docs.madelineproto.xyz/docs/PLUGINS.html)
+ * [Cron](https://docs.madelineproto.xyz/docs/UPDATES.html#cron)
+ * [Persisting data and IPC](https://docs.madelineproto.xyz/docs/UPDATES.html#persisting-data-and-ipc)
+ * [Restarting](https://docs.madelineproto.xyz/docs/UPDATES.html#restarting)
* [Self-restart on webhosts](https://docs.madelineproto.xyz/docs/UPDATES.html#self-restart-on-webhosts)
- * [Async Event driven multi-account](https://docs.madelineproto.xyz/docs/UPDATES.html#async-event-driven-multiaccount)
+ * [Multi-account](https://docs.madelineproto.xyz/docs/UPDATES.html#multiaccount)
* [Webhook (for HTTP APIs)](https://docs.madelineproto.xyz/docs/UPDATES.html#webhook)
* [getUpdates (only for Javascript APIs)](https://docs.madelineproto.xyz/docs/UPDATES.html#getUpdates)
* [Noop (default)](https://docs.madelineproto.xyz/docs/UPDATES.html#noop)
+ * [Simple filters »](https://docs.madelineproto.xyz/docs/FILTERS.html#simple-filters)
+ * [Attribute filters »](https://docs.madelineproto.xyz/docs/FILTERS.html#attribute-filters)
+ * [MTProto filters »](https://docs.madelineproto.xyz/docs/FILTERS.html#mtproto-filters)
* [Filters](https://docs.madelineproto.xyz/docs/FILTERS.html)
* [Simple filters](https://docs.madelineproto.xyz/docs/FILTERS.html#simple-filters)
* [Attribute filters](https://docs.madelineproto.xyz/docs/FILTERS.html#attribute-filters)
@@ -104,10 +117,10 @@ Want to add your own open-source project to this list? [Click here!](https://doc
* [Simple installation](https://docs.madelineproto.xyz/docs/PLUGINS.html#simple-installation)
* [Composer installation](https://docs.madelineproto.xyz/docs/PLUGINS.html#composer-installation)
* [Creating plugins](https://docs.madelineproto.xyz/docs/PLUGINS.html#creating-plugins)
+ * [Full plugin example](https://docs.madelineproto.xyz/docs/PLUGINS.html#full-plugin-example)
* [Limitations](https://docs.madelineproto.xyz/docs/PLUGINS.html#limitations)
* [Namespace requirements](https://docs.madelineproto.xyz/docs/PLUGINS.html#namespace-requirements)
* [Distribution](https://docs.madelineproto.xyz/docs/PLUGINS.html#distribution)
- * [Full plugin example](https://docs.madelineproto.xyz/docs/PLUGINS.html#full-plugin-example)
* [Database](https://docs.madelineproto.xyz/docs/DATABASE.html)
* [\danog\MadelineProto\Settings\Database\Memory: Memory backend settings.](https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/Database/Memory.html)
* [\danog\MadelineProto\Settings\Database\Mysql: MySQL backend settings.](https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/Database/Mysql.html)
@@ -438,6 +451,7 @@ Want to add your own open-source project to this list? [Click here!](https://doc
* Get a payment form: payments.getPaymentForm
* Get a set of suggested custom emoji stickers that can be used as group picture: account.getDefaultGroupPhotoEmojis
* Get a set of suggested custom emoji stickers that can be used as profile picture: account.getDefaultProfilePhotoEmojis
+ * Get admin IDs (equal to the report peers): getAdminIds
* Get all archived stickers: messages.getArchivedStickers
* Get all available chat themes: account.getChatThemes
* Get all contacts: contacts.getSaved
@@ -649,7 +663,6 @@ Want to add your own open-source project to this list? [Click here!](https://doc
* Obtain information about a chat folder deep link ยป: chatlists.checkChatlistInvite
* Obtain information about a named bot web app: messages.getBotApp
* Obtain the API ID UI template: getWebAPITemplate
- * Obtain the ID of the admin of the bot (equal to the first user ID returned by getReportPeers): getAdmin
* Obtain user info from a temporary profile link: contacts.importContactToken
* Obtains a list of messages, indicating to which other public channels was a channel message forwarded. : stats.getMessagePublicForwards
* Obtains a list of peers that can be used to send messages in a specific group: channels.getSendAs
diff --git a/docs b/docs
index 9a73a4a7e..d3eb81be0 160000
--- a/docs
+++ b/docs
@@ -1 +1 @@
-Subproject commit 9a73a4a7e348f9b1b641fdc48198130c62967db9
+Subproject commit d3eb81be0cad837e7fd3f7f6d6e2f7b518b78b55
diff --git a/examples/bot.php b/examples/bot.php
index e05c9e626..dd3071ee0 100755
--- a/examples/bot.php
+++ b/examples/bot.php
@@ -25,8 +25,10 @@ use danog\MadelineProto\Broadcast\Status;
use danog\MadelineProto\EventHandler\Attributes\Cron;
use danog\MadelineProto\EventHandler\Attributes\Handler;
use danog\MadelineProto\EventHandler\Filter\FilterCommand;
+use danog\MadelineProto\EventHandler\Filter\FilterRegex;
use danog\MadelineProto\EventHandler\Filter\FilterText;
use danog\MadelineProto\EventHandler\Message;
+use danog\MadelineProto\EventHandler\SimpleFilter\FromAdmin;
use danog\MadelineProto\EventHandler\SimpleFilter\Incoming;
use danog\MadelineProto\Logger;
use danog\MadelineProto\Settings;
@@ -57,9 +59,7 @@ class MyEventHandler extends SimpleEventHandler
/**
* @var int|string Username or ID of bot admin
*/
- const ADMIN = "@me"; // !!! Change this to your username !!!
-
- private int $adminId;
+ const ADMIN = "@danogentili"; // !!! Change this to your username !!!
/**
* @var array
@@ -90,7 +90,6 @@ class MyEventHandler extends SimpleEventHandler
{
$this->logger("The bot was started!");
$this->logger($this->getFullInfo('MadelineProto'));
- $this->adminId = $this->getId(self::ADMIN);
$this->sendMessageToAdmins("The bot was started!");
}
@@ -118,10 +117,6 @@ class MyEventHandler extends SimpleEventHandler
/**
* Handle incoming updates from users, chats and channels.
- *
- * 100+ other types of onUpdate... method types are available, see https://docs.madelineproto.xyz/API_docs/types/Update.html for the full list.
- * You can also use onAny to catch all update types (only for debugging)
- * A special onUpdateCustomEvent method can also be defined, to send messages to the event handler from an API instance, using the sendCustomEvent method.
*/
#[Handler]
public function handleMessage(Incoming&Message $message): void
@@ -139,32 +134,43 @@ class MyEventHandler extends SimpleEventHandler
}
#[FilterCommand('restart')]
- public function restartCommand(Incoming&Message $message): void
+ public function restartCommand(Incoming & Message & FromAdmin $message): void
{
// If the message is a /restart command from an admin, restart to reload changes to the event handler code.
- if ($message->senderId === $this->adminId) {
- // Make sure to run in a bash while loop when running via CLI to allow self-restarts.
- $this->restart();
- }
+
+ // Make sure to run in a bash while loop when running via CLI to allow self-restarts.
+ $this->restart();
}
#[FilterCommand('broadcast')]
- public function broadcastCommand(Incoming&Message $message): void
+ public function broadcastCommand(Incoming & Message & FromAdmin $message): void
{
// We can broadcast messages to all users.
- if ($message->senderId === $this->adminId) {
- if (!$message->replyToMsgId) {
- $message->reply("You should reply to the message you want to broadcast.");
- return;
- }
- $this->broadcastForwardMessages(
- from_peer: $message->senderId,
- message_ids: [$message->replyToMsgId],
- drop_author: true,
- pin: true,
- );
+ if (!$message->replyToMsgId) {
+ $message->reply("You should reply to the message you want to broadcast.");
return;
}
+ $this->broadcastForwardMessages(
+ from_peer: $message->senderId,
+ message_ids: [$message->replyToMsgId],
+ drop_author: true,
+ pin: true,
+ );
+ }
+
+ #[FilterCommand('echo')]
+ public function echoCmd(Incoming & Message $message): void
+ {
+ // Contains the arguments of the command
+ $args = $message->commandArgs;
+
+ $message->reply($args[0] ?? '');
+ }
+
+ #[FilterRegex('/.*(mt?proto).*/i')]
+ public function testRegex(Incoming & Message $message): void
+ {
+ $message->reply("Did you mean to write MadelineProto instead of ".$message->matches[1].'?');
}
#[FilterText('hi')]
diff --git a/examples/plugins/Danogentili/OnlinePlugin.php b/examples/plugins/Danogentili/OnlinePlugin.php
index 020459f6d..909bfbec4 100644
--- a/examples/plugins/Danogentili/OnlinePlugin.php
+++ b/examples/plugins/Danogentili/OnlinePlugin.php
@@ -1,5 +1,7 @@
getPlugin(PingPlugin::class);
diff --git a/src/EventHandler.php b/src/EventHandler.php
index 4bde41fe4..dfb61e104 100644
--- a/src/EventHandler.php
+++ b/src/EventHandler.php
@@ -29,7 +29,6 @@ use danog\Loop\PeriodicLoop;
use danog\MadelineProto\Db\DbPropertiesTrait;
use danog\MadelineProto\EventHandler\Attributes\Cron;
use danog\MadelineProto\EventHandler\Attributes\Handler;
-use danog\MadelineProto\EventHandler\Attributes\Periodic;
use danog\MadelineProto\EventHandler\Filter\Combinator\FiltersAnd;
use danog\MadelineProto\EventHandler\Filter\Filter;
use danog\MadelineProto\EventHandler\Filter\FilterAllowAll;
@@ -249,7 +248,7 @@ abstract class EventHandler extends AbstractAPI
}
}
/**
- * Obtain a PeriodicLoop instance created by the Periodic attribute.
+ * Obtain a PeriodicLoop instance created by the Cron attribute.
*
* @param string $name Method name
*/
diff --git a/src/InternalDoc.php b/src/InternalDoc.php
index 077b419a4..ba966cd8b 100644
--- a/src/InternalDoc.php
+++ b/src/InternalDoc.php
@@ -640,13 +640,6 @@ abstract class InternalDoc
{
return \danog\MadelineProto\Tools::genVectorHash($ints);
}
- /**
- * Obtain the ID of the admin of the bot (equal to the first user ID returned by getReportPeers).
- */
- public function getAdmin(): int
- {
- return $this->wrapper->getAPI()->getAdmin();
- }
/**
* Get admin IDs (equal to the report peers).
*/
diff --git a/src/MTProto.php b/src/MTProto.php
index 66da66f08..4e8b3ab0b 100644
--- a/src/MTProto.php
+++ b/src/MTProto.php
@@ -1575,10 +1575,6 @@ final class MTProto implements TLCallback, LoggerGetter
* @var list
*/
private array $reportDest = [];
- /**
- * Admin ID.
- */
- private ?int $adminId = null;
/**
* Check if has report peers.
*/
@@ -1659,16 +1655,6 @@ final class MTProto implements TLCallback, LoggerGetter
/** @var array $userOrId */
return \array_values($userOrId);
}
- /**
- * Obtain the ID of the admin of the bot (equal to the first user ID returned by getReportPeers).
- */
- public function getAdmin(): int
- {
- if (!$this->adminId) {
- throw new Exception("No admin ID was set!");
- }
- return $this->adminId;
- }
/**
* Set peer(s) where to send errors occurred in the event loop.
*
@@ -1677,13 +1663,6 @@ final class MTProto implements TLCallback, LoggerGetter
public function setReportPeers(int|string|array $userOrId): void
{
$this->reportDest = $this->sanitizeReportPeers($userOrId);
- $this->adminId = null;
- foreach ($this->reportDest as $id) {
- if ($id > 0) {
- $this->adminId = $id;
- break;
- }
- }
}
private ?LocalMutex $reportMutex = null;
/**
diff --git a/src/MTProtoTools/PeerHandler.php b/src/MTProtoTools/PeerHandler.php
index 84ed08c29..648ba6b53 100644
--- a/src/MTProtoTools/PeerHandler.php
+++ b/src/MTProtoTools/PeerHandler.php
@@ -700,12 +700,6 @@ trait PeerHandler
if ($id === 'me') {
return $this->getInfo($this->authorization['user']['id'], $type);
}
- if ($id === 'admin') {
- if (!isset($this->reportDest[0])) {
- throw new Exception("No report peers were configured, can't send a message to the bot admin!");
- }
- return $this->getInfo($this->reportDest[0], $type);
- }
if ($id === 'support') {
if (!$this->supportUser) {
$this->methodCallAsyncRead('help.getSupport', []);
diff --git a/tools/build_docs.php b/tools/build_docs.php
index 07ccb588e..cd7324a51 100755
--- a/tools/build_docs.php
+++ b/tools/build_docs.php
@@ -140,6 +140,9 @@ function printTypes(array $types, string $type): string
$phpdoc = PhpDoc::fromNamespace();
$data = '';
foreach ($types as $class) {
+ if ($type === 'concretefilters' && $class === Update::class) {
+ continue;
+ }
$refl = new ReflectionClass($class);
$link = "https://docs.madelineproto.xyz/PHP/".str_replace('\\', '/', $class).'.html';
$f = $b->create($refl->getDocComment())->getSummary();
@@ -156,7 +159,7 @@ function printTypes(array $types, string $type): string
continue;
}
$data .= " * [Full property list »]($link#properties)\n";
- $data .= " * [Full method list »]($link#method-list)\n";
+ $data .= " * [Full bound method list »]($link#method-list)\n";
}
return $data;
}
@@ -186,7 +189,9 @@ foreach ($orderedfiles as $key => $filename) {
$result = array_filter($result, fn (string $class) => (new ReflectionClass($class))->getAttributes());
$data = printTypes($result, $match);
} elseif ($match === "mtprotofilters") {
- $data = '';
+ $data = " * onUpdateCustomEvent: Receives messages sent to the event handler from an API instance using the [`sendCustomEvent` »](https://docs.madelineproto.xyz/PHP/danog/MadelineProto/API.html#sendcustomevent-mixed-payload-void) method.\n";
+ $data .= " * onAny: Catch-all filter, if defined catches all updates that aren't catched by any other filter.\n";
+ $data .= " * [onUpdateBroadcastProgress »](https://docs.madelineproto.xyz/docs/BROADCAST.html#get-progress): Used to receive updates to an in-progress [message broadcast »](https://docs.madelineproto.xyz/docs/BROADCAST.html)";
$TL = new TL(null);
$TL->init(new TLSchema);
foreach ($TL->getConstructors()->by_id as $cons) {
@@ -261,6 +266,9 @@ foreach ($orderedfiles as $key => $filename) {
continue;
}
}
+ if (basename($filename) === 'UPDATES.md' && str_starts_with($url, 'https://docs.madelineproto.xyz/PHP/danog/MadelineProto/EventHandler')) {
+ continue;
+ }
$index .= "$spaces* [$name]($url)\n";
if ($name === 'FULL API Documentation with descriptions') {
$spaces .= ' ';