Link Search Menu Expand Document

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:

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 plugin
  • MadelinePlugin\Danogentili\HelperPlugin - A helper plugin
  • MadelinePlugin\Danogentili\CommonTrait - A common trait with some tools, included by the plugins
  • MadelinePlugin\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.

Next section