Plugins
MadelineProto offers a native plugin system, based on event handlers!
Installing plugins
Plugins can be installed by simply placing their files in the plugin path, or by installing them through composer.
Plugins may be run either using a plugin base », or directly (in which case they behave as normal bots).
Simple installation
To install a plugin, copy the plugin’s files into a plugins/
folder, and use the following base code (download »):
<?php declare(strict_types=1);
if (file_exists('vendor/autoload.php')) {
require_once 'vendor/autoload.php';
} else {
// Otherwise download an !!! alpha !!! version of MadelineProto via madeline.php
if (!file_exists('madeline.php')) {
copy('https://phar.madelineproto.xyz/madeline.php', 'madeline.php');
}
require_once 'madeline.php';
}
use danog\MadelineProto\Logger;
use danog\MadelineProto\Settings;
use danog\MadelineProto\Settings\Database\Mysql;
use danog\MadelineProto\Settings\Database\Postgres;
use danog\MadelineProto\Settings\Database\Redis;
use danog\MadelineProto\SimpleEventHandler;
/** Base event handler class that only includes plugins */
class BaseHandler extends SimpleEventHandler
{
public static function getPluginPaths(): array|string|null
{
return 'plugins/';
}
}
$settings = new Settings;
$settings->getLogger()->setLevel(Logger::LEVEL_ULTRA_VERBOSE);
// You can also use Redis, MySQL or PostgreSQL
// $settings->setDb((new Redis)->setDatabase(0)->setPassword('pony'));
// $settings->setDb((new Postgres)->setDatabase('MadelineProto')->setUsername('daniil')->setPassword('pony'));
// $settings->setDb((new Mysql)->setDatabase('MadelineProto')->setUsername('daniil')->setPassword('pony'));
// For users or bots
BaseHandler::startAndLoop('bot.madeline', $settings);
// For bots only
// BaseHandler::startAndLoopBot('bot.madeline', 'bot token', $settings);
Any other folder name may be used as well, and you can also return an array of folder names from getPluginPaths()
.
You can combine plugins installed with this mode with plugins installed using composer.
Composer installation
To install a plugin via composer, require it first:
composer require yournick/someplugin
Then return it from the getPlugins
function of your base event handler:
<?php declare(strict_types=1);
use danog\MadelineProto\Logger;
use danog\MadelineProto\Settings;
use danog\MadelineProto\Settings\Database\Mysql;
use danog\MadelineProto\Settings\Database\Postgres;
use danog\MadelineProto\Settings\Database\Redis;
use danog\MadelineProto\SimpleEventHandler;
use MadelinePlugin\YourNick\PluginName;
/** Base event handler class that includes plugins via Composer */
class BaseHandler extends SimpleEventHandler
{
public static function getPlugins(): array
{
return [PluginName::class]
}
}
$settings = new Settings;
$settings->getLogger()->setLevel(Logger::LEVEL_ULTRA_VERBOSE);
// You can also use Redis, MySQL or PostgreSQL
// $settings->setDb((new Redis)->setDatabase(0)->setPassword('pony'));
// $settings->setDb((new Postgres)->setDatabase('MadelineProto')->setUsername('daniil')->setPassword('pony'));
// $settings->setDb((new Mysql)->setDatabase('MadelineProto')->setUsername('daniil')->setPassword('pony'));
// For users or bots
BaseHandler::startAndLoop('bot.madeline', $settings);
// For bots only
// BaseHandler::startAndLoopBot('bot.madeline', 'bot token', $settings);
You can combine plugins installed with this mode with plugins installed using a plugin path.
Builtin plugins
MadelineProto itself also offers some useful builtin plugins that can be optionally enabled by returning them from the getPlugins
function:
- danog\MadelineProto\EventHandler\Plugin\RestartPlugin » - Plugin that offers a /restart command to admins that can be used to restart the bot, applying changes.
Example:
<?php declare(strict_types=1);
// Simple example bot.
// PHP 8.2.4+ is required.
// Run via CLI (recommended: `screen php bot.php`) or via web.
// To reduce RAM usage, follow these instructions: https://docs.madelineproto.xyz/docs/DATABASE.html
use danog\MadelineProto\EventHandler\Attributes\Handler;
use danog\MadelineProto\EventHandler\Message;
use danog\MadelineProto\EventHandler\Plugin\RestartPlugin;
use danog\MadelineProto\EventHandler\SimpleFilter\Incoming;
use danog\MadelineProto\SimpleEventHandler;
// Load via composer (RECOMMENDED, see https://docs.madelineproto.xyz/docs/INSTALLATION.html#composer-from-scratch)
if (file_exists('vendor/autoload.php')) {
require_once 'vendor/autoload.php';
} else {
// Otherwise download an !!! alpha !!! version of MadelineProto via madeline.php
if (!file_exists('madeline.php')) {
copy('https://phar.madelineproto.xyz/madeline.php', 'madeline.php');
}
require_once 'madeline.php';
}
class BasicEventHandler extends SimpleEventHandler
{
// !!! Change this to your username !!!
public const ADMIN = "@me";
/**
* Get peer(s) where to report errors.
*/
public function getReportPeers()
{
return [self::ADMIN];
}
/**
* Returns a set of plugins to activate.
*
* See here for more info on plugins: https://docs.madelineproto.xyz/docs/PLUGINS.html
*/
public static function getPlugins(): array
{
return [
// Offers a /restart command to admins that can be used to restart the bot, applying changes.
// Make sure to run in a bash while loop when running via CLI to allow self-restarts.
RestartPlugin::class,
];
}
/**
* Handle incoming updates from users, chats and channels.
*/
#[Handler]
public function handleMessage(Incoming&Message $message): void
{
// Code that uses $message...
// See the following pages for more examples and documentation:
// - https://github.com/danog/MadelineProto/blob/v8/examples/bot.php
// - https://docs.madelineproto.xyz/docs/UPDATES.html
// - https://docs.madelineproto.xyz/docs/FILTERS.html
// - https://docs.madelineproto.xyz/
}
}
BasicEventHandler::startAndLoop('bot.madeline');
Creating plugins
To create a plugin, simply create an event handler that extends the PluginEventHandler
class.
Full plugin example
Full example (file name plugins/Danogentili/PingPlugin.php
):
<?php declare(strict_types=1);
namespace MadelinePlugin\Danogentili;
use danog\MadelineProto\EventHandler\Attributes\Cron;
use danog\MadelineProto\EventHandler\Filter\FilterText;
use danog\MadelineProto\EventHandler\Message;
use danog\MadelineProto\EventHandler\SimpleFilter\Incoming;
use danog\MadelineProto\PluginEventHandler;
/**
* Plugin event handler class.
*
* All properties returned by __sleep are automatically stored in the database.
*/
class PingPlugin extends PluginEventHandler
{
private int $pingCount = 0;
private string $pongText = 'pong';
/**
* You can set a custom pong text from the outside of the plugin:.
*
* ```
* if (!file_exists('madeline.php')) {
* copy('https://phar.madelineproto.xyz/madeline.php', 'madeline.php');
* }
* include 'madeline.php';
*
* $a = new API('bot.madeline');
* $plugin = $a->getPlugin(PingPlugin::class);
*
* $plugin->setPongText('UwU');
* ```
*
* This will automatically connect to the running instance of the plugin and call the specified method.
*/
public function setPongText(string $pong): void
{
$this->pongText = $pong;
}
/**
* Returns a list of names for properties that will be automatically saved to the session database (MySQL/postgres/redis if configured, the session file otherwise).
*/
public function __sleep(): array
{
return ['pingCount', 'pongText'];
}
/**
* Initialization logic.
*/
public function onStart(): void
{
$this->logger("The bot was started!");
$this->logger($this->getFullInfo('MadelineProto'));
$this->sendMessageToAdmins("The bot was started!");
}
/**
* Plugins may be enabled or disabled at startup by returning true or false from this function.
*/
public function isPluginEnabled(): bool
{
return true;
}
/**
* This cron function will be executed forever, every 60 seconds.
*/
#[Cron(period: 60.0)]
public function cron1(): void
{
$this->sendMessageToAdmins("The ping plugin is online, total pings so far: ".$this->pingCount);
}
#[FilterText('ping')]
public function pingCommand(Incoming&Message $message): void
{
$message->reply($this->pongText);
$this->pingCount++;
}
}
Plugins may be enabled or disabled at startup by returning true
or false
from isPluginEnabled()
.
Limitations
Plugins can handle updates, include other plugins using getPlugins()
and store persistent data to the session using __sleep
, just like any non-plugin event handler.
However, unlike normal event handlers, plugins can require other plugins using only getPlugins()
, not getPluginPaths()
.
Also, plugins can only interact with the filesystem using MadelineProto’s download and upload functions.
For performance reasons, your plugin should not read files from the filesystem.
Here’s a list of common uses for files, and what they can be replaced with:
Namespace requirements
Plugins must be located in the MadelinePlugin
vendor namespace, preferrably in a custom subnamespace to avoid conflicts with other plugins.
So for example, if your nickname is @danogentili
, use namespace MadelinePlugin\Danogentili;
.
All the classes, interfaces, functions and traits defined by your plugin’s code must be defined in the namespace you chose.
The file layout must follow the PSR-4 autoloading standard; for example, if you have the following classes:
MadelinePlugin\Danogentili\PingPlugin
- A ping pluginMadelinePlugin\Danogentili\HelperPlugin
- A helper pluginMadelinePlugin\Danogentili\CommonTrait
- A common trait with some tools, included by the pluginsMadelinePlugin\Danogentili\Internal\SomeOtherClass
- Some other class (not a plugin)
This will be the directory structure:
plugins/Danogentili/PingPlugin.php
plugins/Danogentili/HelperPlugin.php
plugins/Danogentili/CommonTrait.php
plugins/Danogentili/Internal/SomeOtherClass.php
If you want to define some functions, put them in plugins/Danog/functions.php
(or any subdirectory, as long as the name of the file is functions.php
).
Distribution
Plugins can be distributed in a simple zip file, or using composer.
You can also distribute plugins as standalone files in the MadelinePlugin
namespace (make sure to choose a really unique name!), for example plugins/Danogentili.php
.