MadelineProtoDocs/docs/docs/UPDATES.md

14 KiB

title: Handling updates (new messages) description: Update handling can be done in different ways: image: https://docs.madelineproto.xyz/favicons/android-chrome-256x256.png

Handling updates (new messages)

Update handling can be done in different ways:

Self-restart on webhosts

When running the loop() method via web, MadelineProto will automatically enable a magical self-restart hack, to keep the bot running even on webhosts with limited execution time.

Locking will also be handled automatically (as well as disconnection from the user that opened the page), so even if you start the script via web several times, only one instance will be running at a time (no need to do flocking manually!).

It relies on the shutdown function, so you must not set a custom shutdown function in your code, and instead use the MadelineProto shutdown static API:

use danog\MadelineProto\Shutdown;

$id = Shutdown::addCallback(static function () {
    // This function will run on shutdown
});

$id = Shutdown::addCallback(static function () {
    // This function will run on shutdown
}, 'custom id');

$id = Shutdown::addCallback(static function () {
    // This function will overwrite the previously set function with custom id
}, 'custom id');

$ok = Shutdown::removeCallback($id);

You can of course pass non-static functions, any type of callable is accepted.
A second optional parameter can also be accepted, containing the ID of the callable: you can use this if you want to later overwrite the callable with another callback, or remove it altogether.

The removeCallback will return true if the callback exists and it was removed correctly, false otherwise (as with all new MadelineProto 4.0 APIs, there are PHPDOCs for these methods so you'll see them in your IDE).

Async Event driven

class EventHandler extends \danog\MadelineProto\EventHandler
{
    public function __construct($MadelineProto)
    {
        parent::__construct($MadelineProto);
    }
    public function onUpdateSomethingElse($update)
    {
        // See the docs for a full list of updates: http://docs.madelineproto.xyz/API_docs/types/Update.html
    }
    public function onUpdateNewChannelMessage($update)
    {
        yield $this->onUpdateNewMessage($update);
    }
    public function onUpdateNewMessage($update)
    {
        if (isset($update['message']['out']) && $update['message']['out']) {
            return;
        }
        $res = json_encode($update, JSON_PRETTY_PRINT);
        if ($res == '') {
            $res = var_export($update, true);
        }

        try {
            yield $this->messages->sendMessage(['peer' => $update, 'message' => $res, 'reply_to_msg_id' => $update['message']['id']]);
        } catch (\danog\MadelineProto\RPCErrorException $e) {
            yield $this->messages->sendMessage(['peer' => '@danogentili', 'message' => (string) $e]);
        }

        try {
            if (isset($update['message']['media']) && ($update['message']['media']['_'] == 'messageMediaPhoto' || $update['message']['media']['_'] == 'messageMediaDocument')) {
                $time = microtime(true);
                $file = yield $this->downloadToDir($update, '/tmp');
                yield $this->messages->sendMessage(['peer' => $update, 'message' => 'Downloaded to '.$file.' in '.(microtime(true) - $time).' seconds', 'reply_to_msg_id' => $update['message']['id']]);
            }
        } catch (\danog\MadelineProto\RPCErrorException $e) {
            yield $this->messages->sendMessage(['peer' => '@danogentili', 'message' => (string) $e]);
        }
    }
}


$MadelineProto = new \danog\MadelineProto\API('bot.madeline');
$MadelineProto->async(true);
$MadelineProto->loop(function () use ($MadelineProto) {
    yield $MadelineProto->start();
    yield $MadelineProto->setEventHandler('\EventHandler');
});
$MadelineProto->loop();

This will create an event handler class EventHandler, create a MadelineProto session, and set the event handler class to our newly created event handler.

This yield syntax might be new to you, even if you already used MadelineProto in the past.
It's a new syntax to allow async parallel processing of updates and HUGE speed improvements.
It was recently introduced in MadelineProto, here's a full explanation.
If your code still relies on the old synchronous behaviour, it's still supported, but I HIGHLY recommend you switch to the new async syntax: it's super easy, just add a yield in front of method calls!

When an Update is received, the corresponding onUpdateType event handler method is called.

To get a list of all possible update types, click here.

If such a method does not exist, the onAny event handler method is called.
If the onAny event handler method does not exist, the update is ignored.
The onLoop method is not recommended anymore, use AMPHP's repeat or MadelineProto's async loop API to schedule actions in a cron-like manner.

To access the $MadelineProto instance inside of the event handler, simply access $this:

$this->messages->sendMessage(['peer' => '@danogentili', 'message' => 'hi']);

If you intend to use your own constructor in the event handler, make sure to call the parent construtor with the only parameter provided to your constructor.

The update handling loop is started by the $MadelineProto->loop() method, and it will automatically restart the script if execution time runs out.

To break out of the loop just call die();, or throw an exception from within (make sure to catch it outside, in the $MadelineProto->loop() call).

Async Combined event driven

class EventHandler extends \danog\MadelineProto\CombinedEventHandler
{
    public function __construct($CombinedMadelineProto)
    {
        parent::__construct($CombinedMadelineProto);
    }
    public function onUpdateSomethingElse($update, $session)
    {
        // See the docs for a full list of updates: http://docs.madelineproto.xyz/API_docs/types/Update.html
    }
    public function onUpdateNewChannelMessage($update, $session)
    {
        yield $this->onUpdateNewMessage($update, $session);
    }
    public function onUpdateNewMessage($update, $session)
    {
        if (isset($update['message']['out']) && $update['message']['out']) {
            return;
        }
        $res = json_encode($update, JSON_PRETTY_PRINT);
        if ($res == '') {
            $res = var_export($update, true);
        }

        try {
            yield $this->{$session}->messages->sendMessage(['peer' => $update, 'message' => $res, 'reply_to_msg_id' => $update['message']['id']]);
        } catch (\danog\MadelineProto\RPCErrorException $e) {
            yield $this->{$session}->messages->sendMessage(['peer' => '@danogentili', 'message' => (string) $e]);
        }

        try {
            if (isset($update['message']['media']) && ($update['message']['media']['_'] == 'messageMediaPhoto' || $update['message']['media']['_'] == 'messageMediaDocument')) {
                $time = microtime(true);
                $file = yield $this->{$session}->downloadToDir($update, '/tmp');
                yield $this->{$session}->messages->sendMessage(['peer' => $update, 'message' => 'Downloaded to '.$file.' in '.(microtime(true) - $time).' seconds', 'reply_to_msg_id' => $update['message']['id']]);
            }
        } catch (\danog\MadelineProto\RPCErrorException $e) {
            yield $this->{$session}->messages->sendMessage(['peer' => '@danogentili', 'message' => (string) $e]);
        }
    }
}

$settings = [];
$CombinedMadelineProto = new \danog\MadelineProto\CombinedAPI('combined_session.madeline', ['bot.madeline' => $settings, 'user.madeline' => $settings, 'user2.madeline' => $settings]);
$CombinedMadelineProto->async(true);
$CombinedMadelineProto->loop(function () use ($CombinedMadelineProto) {
    $res = [];
    foreach ([
        'bot.madeline' => 'Bot Login',
        'user.madeline' => 'Userbot login',
        'user2.madeline' => 'Userbot login (2)'
    ] as $session => $message) {
        \danog\MadelineProto\Logger::log($message, \danog\MadelineProto\Logger::WARNING);
        $res []= $CombinedMadelineProto->instances[$session]->start();
    }
    yield $CombinedMadelineProto->all($res);
    yield $CombinedMadelineProto->setEventHandler('\EventHandler');
});
$CombinedMadelineProto->loop();

This will create an event handler class EventHandler, create a combined MadelineProto session with session files bot.madeline, user.madeline, user2.madeline, and set the event handler class to our newly created event handler.

This yield syntax might be new to you, even if you already used MadelineProto in the past.
It's a new syntax to allow async parallel processing of updates and HUGE speed improvements.
It was recently introduced in MadelineProto, here's a full explanation.
If your code still relies on the old synchronous behaviour, it's still supported, but I HIGHLY recommend you switch to the new async syntax: it's super easy, just add a yield in front of method calls!

When an Update is received, the corresponding onUpdateType event handler method is called.

To get a list of all possible update types, click here.

If such a method does not exist, the onAny event handler method is called.
If the onAny event handler method does not exist, the update is ignored.
The first paramter of the event handler method will always be the Update, the second parameter will always be the session name.

The onLoop method is not recommended anymore, use AMPHP's repeat or MadelineProto's async loop API to schedule actions in a cron-like manner.

To access the $MadelineProto instance of the account that sent the update, from inside of the event handler, simply access $this->{$session_name} ($session_name is the second parameter value of the event handler method, or just the session filename):

$this->{$session_name}->messages->sendMessage(['peer' => '@danogentili', 'message' => 'hi']);
$this->{'user2.madeline'}->messages->sendMessage(['peer' => '@danogentili', 'message' => 'hi2']);

If you intend to use your own constructor in the event handler, make sure to call the parent construtor with the only parameter provided to your constructor.

If you need to use the __sleep function, make sure it is called __magic_sleep, instead.

The update handling loop is started by the $MadelineProto->loop() method, and it will automatically restart the script if execution time runs out.

To break out of the loop just call die();, or throw an exception from within (make sure to catch it outside, in the $MadelineProto->loop() call).

Async callback

$MadelineProto = new \danog\MadelineProto\API('bot.madeline');

$MadelineProto->start();
$MadelineProto->setCallback(function ($update) use ($MadelineProto) {

    if (isset($update['message']['out']) && $update['message']['out']) {
        return;
    }
    $res = json_encode($update, JSON_PRETTY_PRINT);
    if ($res == '') {
        $res = var_export($update, true);
    }

    try {
        yield $MadelineProto->messages->sendMessage(['peer' => $update, 'message' => $res, 'reply_to_msg_id' => $update['message']['id']]);
    } catch (\danog\MadelineProto\RPCErrorException $e) {
        yield $MadelineProto->messages->sendMessage(['peer' => '@danogentili', 'message' => (string) $e]);
    }
});
$MadelineProto->async(true);
$MadelineProto->loop();

When an Update is received, the provided callback function is called.

The update handling loop is started by the $MadelineProto->loop() method, and it will automatically restart the script if execution time runs out.

This yield syntax might be new to you, even if you already used MadelineProto in the past.
It's a new syntax to allow async parallel processing of updates and HUGE speed improvements.
It was recently introduced in MadelineProto, here's a full explanation.
If your code still relies on the old synchronous behaviour, it's still supported, but I HIGHLY recommend you switch to the new async syntax: it's super easy, just add a yield in front of method calls!

To break out of the loop just call die();, or throw an exception from within (make sure to catch it outside, in the $MadelineProto->loop() call).

Noop

$MadelineProto = new \danog\MadelineProto\API('bot.madeline');

$MadelineProto->start();
$MadelineProto->setNoop();

When an Update is received, nothing is done. This is useful if you need to populate the internal peer database with peers to avoid This peer is not present in the internal peer database errors, but don't need to handle updates.
This is the default.

Fetch all updates from the beginning

You can use the resetUpdateState method to reset the update state and fetch all updates from the beginning:

$MadelineProto->resetUpdateState();

Next section