Add native error reporting functions and slash boilerplate

This commit is contained in:
Daniil Gentili 2020-02-23 19:28:42 +01:00
parent 0aa75af3e4
commit c3270a5c63
Signed by: danog
GPG Key ID: 8C1BE3B34B230CA7
14 changed files with 448 additions and 172 deletions

View File

@ -19,7 +19,12 @@
* @link https://docs.madelineproto.xyz MadelineProto documentation * @link https://docs.madelineproto.xyz MadelineProto documentation
*/ */
/* use danog\MadelineProto\API;
use danog\MadelineProto\EventHandler;
use danog\MadelineProto\Exception;
use danog\MadelineProto\RPCErrorException;
/*
* Various ways to load MadelineProto * Various ways to load MadelineProto
*/ */
if (\file_exists('vendor/autoload.php')) { if (\file_exists('vendor/autoload.php')) {
@ -34,13 +39,40 @@ if (\file_exists('vendor/autoload.php')) {
/** /**
* Event handler class. * Event handler class.
*/ */
class EventHandler extends \danog\MadelineProto\EventHandler class MyEventHandler extends EventHandler
{ {
public function onUpdateNewChannelMessage($update) /**
* @var int|string Username or ID of bot admin
*/
const ADMIN = "danogentili"; // Change this
/**
* Get peer(s) where to report errors.
*
* @return int|string|array
*/
public function getReportPeers()
{ {
yield $this->onUpdateNewMessage($update); return [self::ADMIN];
} }
public function onUpdateNewMessage($update) /**
* Handle updates from supergroups and channels.
*
* @param array $update Update
*
* @return void
*/
public function onUpdateNewChannelMessage(array $update): \Generator
{
return $this->onUpdateNewMessage($update);
}
/**
* Handle updates from users.
*
* @param array $update Update
*
* @return \Generator
*/
public function onUpdateNewMessage(array $update): \Generator
{ {
if ($update['message']['_'] === 'messageEmpty' || $update['message']['out'] ?? false) { if ($update['message']['_'] === 'messageEmpty' || $update['message']['out'] ?? false) {
return; return;
@ -51,21 +83,13 @@ class EventHandler extends \danog\MadelineProto\EventHandler
yield $this->messages->sendMessage(['peer' => $update, 'message' => "<code>$res</code>", 'reply_to_msg_id' => isset($update['message']['id']) ? $update['message']['id'] : null, 'parse_mode' => 'HTML']); yield $this->messages->sendMessage(['peer' => $update, 'message' => "<code>$res</code>", 'reply_to_msg_id' => isset($update['message']['id']) ? $update['message']['id'] : null, 'parse_mode' => 'HTML']);
if (isset($update['message']['media']) && $update['message']['media']['_'] !== 'messageMediaGame') { if (isset($update['message']['media']) && $update['message']['media']['_'] !== 'messageMediaGame') {
yield $this->messages->sendMedia(['peer' => $update, 'message' => $update['message']['message'], 'media' => $update]); yield $this->messages->sendMedia(['peer' => $update, 'message' => $update['message']['message'], 'media' => $update]);
/* '_' => 'inputMediaUploadedDocument',
'file' => $update,
'attributes' => [
['_' => 'documentAttributeFilename', 'file_name' => 'document.txt']
]
],]);*/
//yield $this->downloadToDir($update, '/tmp');
} }
} catch (\danog\MadelineProto\RPCErrorException $e) { } catch (RPCErrorException $e) {
$this->logger((string) $e, \danog\MadelineProto\Logger::FATAL_ERROR); $this->report("Surfaced: $e");
} catch (\danog\MadelineProto\Exception $e) { } catch (Exception $e) {
if (\stripos($e->getMessage(), 'invalid constructor given') === false) { if (\stripos($e->getMessage(), 'invalid constructor given') === false) {
$this->logger((string) $e, \danog\MadelineProto\Logger::FATAL_ERROR); $this->report("Surfaced: $e");
} }
//$this->messages->sendMessage(['peer' => '@danogentili', 'message' => $e->getCode().': '.$e->getMessage().PHP_EOL.$e->getTraceAsString()]);
} }
} }
} }
@ -78,11 +102,8 @@ $settings = [
], ],
]; ];
$MadelineProto = new \danog\MadelineProto\API('bot.madeline', $settings); $MadelineProto = new API('bot.madeline', $settings);
$MadelineProto->async(true);
$MadelineProto->loop(function () use ($MadelineProto) {
yield $MadelineProto->start();
yield $MadelineProto->setEventHandler('\EventHandler');
});
$MadelineProto->loop(); // Reduce boilerplate with new wrapper method.
// Also initializes error reporting, catching and reporting all errors surfacing from the event loop.
$MadelineProto->startAndLoop(MyEventHandler::class);

View File

@ -19,7 +19,12 @@
* @link https://docs.madelineproto.xyz MadelineProto documentation * @link https://docs.madelineproto.xyz MadelineProto documentation
*/ */
\set_include_path(\get_include_path().':'.\realpath(\dirname(__FILE__).'/MadelineProto/')); use danog\MadelineProto\API;
use danog\MadelineProto\EventHandler;
use danog\MadelineProto\Exception;
use danog\MadelineProto\Logger;
use danog\MadelineProto\RPCErrorException;
use danog\MadelineProto\Tools;
/* /*
* Various ways to load MadelineProto * Various ways to load MadelineProto
@ -34,54 +39,89 @@ if (\file_exists(__DIR__.'/vendor/autoload.php')) {
} }
/** /**
* Combined event handler class. * Event handler class.
*/ */
class EventHandler extends \danog\MadelineProto\CombinedEventHandler class MyEventHandler extends EventHandler
{ {
public function onUpdateNewChannelMessage($update, $path) /**
* @var int|string Username or ID of bot admin
*/
const ADMIN = "danogentili"; // Change this
/**
* Get peer(s) where to report errors.
*
* @return int|string|array
*/
public function getReportPeers()
{ {
yield $this->onUpdateNewMessage($update, $path); return [self::ADMIN];
} }
public function onUpdateNewMessage($update, $path) /**
* Handle updates from supergroups and channels.
*
* @param array $update Update
*
* @return void
*/
public function onUpdateNewChannelMessage(array $update): \Generator
{ {
if (isset($update['message']['out']) && $update['message']['out']) { return $this->onUpdateNewMessage($update);
}
/**
* Handle updates from users.
*
* @param array $update Update
*
* @return \Generator
*/
public function onUpdateNewMessage(array $update): \Generator
{
if ($update['message']['_'] === 'messageEmpty' || $update['message']['out'] ?? false) {
return; return;
} }
$MadelineProto = $this->{$path};
if (isset($update['message']['media'])) {
yield $MadelineProto->messages->sendMedia(['peer' => $update, 'message' => $update['message']['message'], 'media' => $update]);
}
$res = \json_encode($update, JSON_PRETTY_PRINT); $res = \json_encode($update, JSON_PRETTY_PRINT);
if ($res == '') {
$res = \var_export($update, true);
}
yield $MadelineProto->sleep(3);
try { try {
yield $MadelineProto->messages->sendMessage(['peer' => $update, 'message' => "<code>$res</code>\n\nDopo 3 secondi, in modo asincrono", 'reply_to_msg_id' => isset($update['message']['id']) ? $update['message']['id'] : null, 'parse_mode' => 'HTML']); //'entities' => [['_' => 'messageEntityPre', 'offset' => 0, 'length' => strlen($res), 'language' => 'json']]]); yield $this->messages->sendMessage(['peer' => $update, 'message' => "<code>$res</code>", 'reply_to_msg_id' => isset($update['message']['id']) ? $update['message']['id'] : null, 'parse_mode' => 'HTML']);
} catch (\danog\MadelineProto\RPCErrorException $e) { if (isset($update['message']['media']) && $update['message']['media']['_'] !== 'messageMediaGame') {
\danog\MadelineProto\Logger::log((string) $e, \danog\MadelineProto\Logger::FATAL_ERROR); yield $this->messages->sendMedia(['peer' => $update, 'message' => $update['message']['message'], 'media' => $update]);
} catch (\danog\MadelineProto\Exception $e) { }
\danog\MadelineProto\Logger::log((string) $e, \danog\MadelineProto\Logger::FATAL_ERROR); } catch (RPCErrorException $e) {
//$MadelineProto->messages->sendMessage(['peer' => '@danogentili', 'message' => $e->getCode().': '.$e->getMessage().PHP_EOL.$e->getTraceAsString()]); $this->report("Surfaced: $e");
} catch (Exception $e) {
if (\stripos($e->getMessage(), 'invalid constructor given') === false) {
$this->report("Surfaced: $e");
}
} }
} }
} }
$settings = ['logger' => ['logger_level' => 5]]; $MadelineProtos = [];
$CombinedMadelineProto = new \danog\MadelineProto\CombinedAPI('combined_session.madeline', ['bot.madeline' => $settings, 'user.madeline' => $settings]); foreach ([
'bot.madeline' => 'Bot Login',
'user.madeline' => 'Userbot login',
'user2.madeline' => 'Userbot login (2)'
] as $session => $message) {
Logger::log($message, Logger::WARNING);
$MadelineProto = new API($session);
$MadelineProto->async(true);
$MadelineProto->loop(function () use ($MadelineProto) {
yield $MadelineProto->start();
yield $MadelineProto->setEventHandler(MyEventHandler::class);
});
$MadelineProtos []= $MadelineProto->loopFork();
}
\danog\MadelineProto\Logger::log('Bot login', \danog\MadelineProto\Logger::WARNING); do {
$CombinedMadelineProto->instances['bot.madeline']->start(); $thrown = false;
try {
\danog\MadelineProto\Logger::log('Userbot login'); Tools::wait(Tools::all($MadelineProtos));
$CombinedMadelineProto->instances['user.madeline']->start(); } catch (\Throwable $e) {
$thrown = true;
$CombinedMadelineProto->setEventHandler('\EventHandler'); try {
$CombinedMadelineProto->loop(); $MadelineProto->report("Surfaced: $e");
} catch (\Throwable $e) {
$CombinedMadelineProto->async(true); $MadelineProto->logger((string) $e, \danog\MadelineProto\Logger::FATAL_ERROR);
$CombinedMadelineProto->setEventHandler('\EventHandler'); }
$CombinedMadelineProto->loop(); }
} while ($thrown);

View File

@ -21,7 +21,6 @@
use Amp\Http\Server\HttpServer; use Amp\Http\Server\HttpServer;
use danog\MadelineProto\API; use danog\MadelineProto\API;
use danog\MadelineProto\Logger;
use danog\MadelineProto\MTProtoTools\Files; use danog\MadelineProto\MTProtoTools\Files;
use danog\MadelineProto\RPCErrorException; use danog\MadelineProto\RPCErrorException;
use danog\MadelineProto\Tools; use danog\MadelineProto\Tools;
@ -49,7 +48,21 @@ class EventHandler extends \danog\MadelineProto\EventHandler
"Usage: `https://example.com file name.ext`\n\n". "Usage: `https://example.com file name.ext`\n\n".
"I can also rename Telegram files, just send me any file and I will rename it!\n\n". "I can also rename Telegram files, just send me any file and I will rename it!\n\n".
"Max 1.5GB, parallel upload and download powered by @MadelineProto."; "Max 1.5GB, parallel upload and download powered by @MadelineProto.";
const ADMIN = 'danogentili';
/**
* @var int|string Username or ID of bot admin
*/
const ADMIN = 'danogentili'; // Change this
/**
* Get peer(s) where to report errors.
*
* @return int|string|array
*/
public function getReportPeers()
{
return [self::ADMIN];
}
/** /**
* Whether to allow uploads. * Whether to allow uploads.
@ -65,26 +78,32 @@ class EventHandler extends \danog\MadelineProto\EventHandler
/** /**
* Constructor. * Constructor.
* *
* @param API $API API * @param ?API $API API
*/ */
public function __construct($API) public function __construct(?API $API)
{ {
$this->UPLOAD = \class_exists(HttpServer::class); $this->UPLOAD = \class_exists(HttpServer::class);
parent::__construct($API); parent::__construct($API);
} }
public function onUpdateNewChannelMessage($update) /**
* Handle updates from channels and supergroups.
*
* @param array $update Update
*
* @return \Generator
*/
public function onUpdateNewChannelMessage(array $update)
{ {
//yield $this->onUpdateNewMessage($update); //yield $this->onUpdateNewMessage($update);
} }
public function report(string $message) /**
{ * Handle updates from users.
try { *
$this->messages->sendMessage(['peer' => self::ADMIN, 'message' => $message]); * @param array $update Update
} catch (\Throwable $e) { *
$this->logger("While reporting: $e", Logger::FATAL_ERROR); * @return \Generator
} */
} public function onUpdateNewMessage(array $update): \Generator
public function onUpdateNewMessage($update)
{ {
if ($update['message']['out'] ?? false) { if ($update['message']['out'] ?? false) {
return; return;
@ -111,7 +130,7 @@ class EventHandler extends \danog\MadelineProto\EventHandler
unset($this->states[$peerId]); unset($this->states[$peerId]);
$update = Files::extractBotAPIFile(yield $this->MTProtoToBotAPI($update)); $update = Files::extractBotAPIFile(yield $this->MTProtoToBotAPI($update));
$file = [$update['file_size'], $update['mime_type']]; $file = [$update['file_size'], $update['mime_type']];
\var_dump($update['file_id'].'.'.Tools::base64urlEncode(json_encode($file))."/".$update['file_name']); \var_dump($update['file_id'].'.'.Tools::base64urlEncode(\json_encode($file))."/".$update['file_name']);
return; return;
} }
yield $this->messages->sendMessage(['peer' => $peerId, 'message' => 'Give me a new name for this file: ', 'reply_to_msg_id' => $messageId]); yield $this->messages->sendMessage(['peer' => $peerId, 'message' => 'Give me a new name for this file: ', 'reply_to_msg_id' => $messageId]);
@ -220,19 +239,7 @@ $settings = [
]; ];
$MadelineProto = new \danog\MadelineProto\API(($argv[1] ?? 'bot').'.madeline', $settings); $MadelineProto = new \danog\MadelineProto\API(($argv[1] ?? 'bot').'.madeline', $settings);
$MadelineProto->async(true);
while (true) { // Reduce boilerplate with new wrapper method.
try { // Also initializes error reporting, catching and reporting all errors surfacing from the event loop.
$MadelineProto->loop(function () use ($MadelineProto) { $MadelineProto->startAndLoop(MyEventHandler::class);
yield $MadelineProto->start();
yield $MadelineProto->setEventHandler('\EventHandler');
});
$MadelineProto->loop();
} catch (\Throwable $e) {
try {
$MadelineProto->logger("Surfaced: $e");
$MadelineProto->getEventHandler(['async' => false])->report("Surfaced: $e");
} catch (\Throwable $e) {
}
}
}

View File

@ -33,11 +33,48 @@ if (\file_exists(__DIR__.'/../vendor/autoload.php')) {
include 'madeline.php'; include 'madeline.php';
} }
class EventHandler extends \danog\MadelineProto\EventHandler class SecretHandler extends \danog\MadelineProto\EventHandler
{ {
private $sent = [-440592694 => true]; private $sent = [-440592694 => true];
public function __construct($API)
public function onUpdateNewEncryptedMessage($update) {
parent::__construct($API);
$this->sent = [];
}
/**
* @var int|string Username or ID of bot admin
*/
const ADMIN = "danogentili"; // Change this
/**
* Get peer(s) where to report errors.
*
* @return int|string|array
*/
public function getReportPeers()
{
return [self::ADMIN];
}
/**
* Handle updates from users.
*
* @param array $update Update
*
* @return \Generator
*/
public function onUpdateNewMessage(array $update): \Generator
{
if ($update['message']['message'] === 'request') {
yield $this->requestSecretChat($update);
}
}
/**
* Handle secret chat messages.
*
* @param array $update Update
*
* @return \Generator
*/
public function onUpdateNewEncryptedMessage(array $update): \Generator
{ {
try { try {
if (isset($update['message']['decrypted_message']['media'])) { if (isset($update['message']['decrypted_message']['media'])) {
@ -72,14 +109,14 @@ class EventHandler extends \danog\MadelineProto\EventHandler
$secret_media['voice'] = ['peer' => $update, 'file' => 'tests/mosconi.mp3', 'message' => ['_' => 'decryptedMessage', 'ttl' => 0, 'message' => '', 'media' => ['_' => 'decryptedMessageMediaDocument', 'thumb' => \file_get_contents('tests/faust.preview.jpg'), 'thumb_w' => 90, 'thumb_h' => 90, 'mime_type' => \mime_content_type('tests/mosconi.mp3'), 'caption' => 'test', 'file_name' => 'mosconi.mp3', 'size' => \filesize('tests/mosconi.mp3'), 'attributes' => [['_' => 'documentAttributeAudio', 'voice' => true, 'duration' => 1, 'title' => 'AH NON LO SO IO', 'performer' => 'IL DIO GERMANO MOSCONI']]]]]; $secret_media['voice'] = ['peer' => $update, 'file' => 'tests/mosconi.mp3', 'message' => ['_' => 'decryptedMessage', 'ttl' => 0, 'message' => '', 'media' => ['_' => 'decryptedMessageMediaDocument', 'thumb' => \file_get_contents('tests/faust.preview.jpg'), 'thumb_w' => 90, 'thumb_h' => 90, 'mime_type' => \mime_content_type('tests/mosconi.mp3'), 'caption' => 'test', 'file_name' => 'mosconi.mp3', 'size' => \filesize('tests/mosconi.mp3'), 'attributes' => [['_' => 'documentAttributeAudio', 'voice' => true, 'duration' => 1, 'title' => 'AH NON LO SO IO', 'performer' => 'IL DIO GERMANO MOSCONI']]]]];
foreach ($secret_media as $type => $smessage) { foreach ($secret_media as $type => $smessage) {
yield $this->messages->sendEncryptedFile($smessage); $promises = $this->messages->sendEncryptedFile($smessage);
} }
yield $promises;
$i = 0; $i = 0;
while ($i < 10) { while ($i < 10) {
$this->logger("SENDING MESSAGE $i TO ".$update['message']['chat_id']); $this->logger("SENDING MESSAGE $i TO ".$update['message']['chat_id']);
// You can also use the sendEncrypted parameter for more options in secret chats // You can also use the sendEncrypted parameter for more options in secret chats
//yield $this->messages->sendEncrypted(['peer' => $update, 'message' => ['_' => 'decryptedMessage', 'ttl' => 0, 'message' => (string) ($i++)]]);
yield $this->messages->sendMessage(['peer' => $update, 'message' => (string) ($i++)]); yield $this->messages->sendMessage(['peer' => $update, 'message' => (string) ($i++)]);
} }
$this->sent[$update['message']['chat_id']] = true; $this->sent[$update['message']['chat_id']] = true;
@ -102,7 +139,5 @@ $settings = \json_decode(\getenv('MTPROTO_SETTINGS'), true) ?: [];
$MadelineProto = new \danog\MadelineProto\API('s.madeline', $settings); $MadelineProto = new \danog\MadelineProto\API('s.madeline', $settings);
$MadelineProto->start(); // Reduce boilerplate with new wrapper method
$MadelineProto->setEventHandler('\EventHandler'); $MadelineProto->startAndLoop(MyEventHandler::class);
$MadelineProto->async(true);
$MadelineProto->loop();

View File

@ -405,4 +405,35 @@ class API extends InternalDoc
return $wrote; return $wrote;
})()); })());
} }
/**
* Start MadelineProto and the event handler (enables async).
*
* Also initializes error reporting, catching and reporting all errors surfacing from the event loop.
*
* @param string $eventHandler Event handler class name
*
* @return void
*/
public function startAndLoop(string $eventHandler): void
{
$this->async(true);
do {
$thrown = false;
try {
$this->loop(function () use ($eventHandler) {
yield $this->start();
yield $this->setEventHandler($eventHandler);
});
$this->loop();
} catch (\Throwable $e) {
$thrown = true;
try {
$this->report("Surfaced: $e");
} catch (\Throwable $e) {
$this->logger((string) $e, Logger::FATAL_ERROR);
}
}
} while ($thrown);
}
} }

View File

@ -97,15 +97,15 @@ abstract class AbstractAPIFactory extends AsyncConstruct
public function __call(string $name, array $arguments) public function __call(string $name, array $arguments)
{ {
$yielded = Tools::call($this->__call_async($name, $arguments)); $yielded = Tools::call($this->__call_async($name, $arguments));
$async = !$this->lua && ((is_array(\end($arguments)) ? \end($arguments) : [])['async'] ?? ($this->async && $name !== 'loop')); $async = !$this->lua && ((\is_array(\end($arguments)) ? \end($arguments) : [])['async'] ?? ($this->async && $name !== 'loop'));
if ($async) { if ($async) {
return $yielded; return $yielded;
} }
$yielded = Tools::wait($yielded);
if (!$this->lua) { if (!$this->lua) {
return Tools::wait($yielded); return $yielded;
} }
try { try {
$yielded = Tools::wait($yielded);
Lua::convertObjects($yielded); Lua::convertObjects($yielded);
return $yielded; return $yielded;
} catch (\Throwable $e) { } catch (\Throwable $e) {
@ -128,9 +128,6 @@ abstract class AbstractAPIFactory extends AsyncConstruct
yield from $this->initAsynchronously(); yield from $this->initAsynchronously();
$this->API->logger->logger('Finished init asynchronously'); $this->API->logger->logger('Finished init asynchronously');
} }
if (Magic::isFork() && !Magic::$processed_fork) {
throw new Exception('Forking not supported, use async logic, instead: https://docs.madelineproto.xyz/docs/ASYNC.html');
}
if (!$this->API) { if (!$this->API) {
throw new Exception('API did not init!'); throw new Exception('API did not init!');
} }
@ -160,7 +157,7 @@ abstract class AbstractAPIFactory extends AsyncConstruct
$args = isset($arguments[0]) && \is_array($arguments[0]) ? $arguments[0] : []; $args = isset($arguments[0]) && \is_array($arguments[0]) ? $arguments[0] : [];
return yield from $this->API->methodCallAsyncRead($name, $args, $aargs); return yield from $this->API->methodCallAsyncRead($name, $args, $aargs);
} }
$res = yield $this->methods[$lower_name](...$arguments); $res = $this->methods[$lower_name](...$arguments);
return $res instanceof \Generator ? yield from $res : yield $res; return $res instanceof \Generator ? yield from $res : yield $res;
} }
/** /**

View File

@ -41,4 +41,13 @@ class EventHandler extends InternalDoc
$this->{$namespace} = new APIFactory($namespace, $this->API, $this->async); $this->{$namespace} = new APIFactory($namespace, $this->API, $this->async);
} }
} }
/**
* Get peers where to send error reports.
*
* @return array|string|int
*/
public function getReportPeers()
{
return [];
}
} }

View File

@ -4215,6 +4215,37 @@ class InternalDoc extends APIFactory
{ {
return $this->__call(__FUNCTION__, [$extra]); return $this->__call(__FUNCTION__, [$extra]);
} }
/**
* Check if has report peers.
*
* @return boolean
*/
public function hasReportPeers(): bool
{
return $this->API->hasReportPeers();
}
/**
* Set peer(s) where to send errors occurred in the event loop.
*
* @param int|string $userOrId Username(s) or peer ID(s)
*
* @return \Generator
*/
public function setReportPeers($userOrId, array $extra = [])
{
return $this->__call(__FUNCTION__, [$userOrId, $extra]);
}
/**
* Report an error to the previously set peer.
*
* @param string $message Error to report
*
* @return \Generator
*/
public function report(string $message, array $extra = [])
{
return $this->__call(__FUNCTION__, [$message, $extra]);
}
/** /**
* Call method and wait asynchronously for response. * Call method and wait asynchronously for response.
* *
@ -5293,6 +5324,17 @@ class InternalDoc extends APIFactory
{ {
return \danog\MadelineProto\MTProto::rleEncode($string); return \danog\MadelineProto\MTProto::rleEncode($string);
} }
/**
* Inflate stripped photosize to full JPG payload.
*
* @param string $stripped Stripped photosize
*
* @return string JPG payload
*/
public function inflateStripped(string $stripped): string
{
return \danog\MadelineProto\MTProto::inflateStripped($stripped);
}
/** /**
* Get final element of array. * Get final element of array.
* *
@ -5460,6 +5502,17 @@ class InternalDoc extends APIFactory
{ {
$this->API->setEventHandler($event_handler); $this->API->setEventHandler($event_handler);
} }
/**
* Unset event handler.
*
* @param bool $disableUpdateHandling Whether to also disable internal update handling (will cause errors, otherwise will simply use the NOOP handler)
*
* @return void
*/
public function unsetEventHandler(bool $disableUpdateHandling = false): void
{
$this->API->unsetEventHandler($disableUpdateHandling);
}
/** /**
* Get event handler. * Get event handler.
* *
@ -5469,6 +5522,15 @@ class InternalDoc extends APIFactory
{ {
return $this->API->getEventHandler(); return $this->API->getEventHandler();
} }
/**
* Check if an event handler instance is present.
*
* @return boolean
*/
public function hasEventHandler(): bool
{
return $this->API->hasEventHandler();
}
/** /**
* Set webhook update handler. * Set webhook update handler.
* *
@ -5591,17 +5653,6 @@ class InternalDoc extends APIFactory
{ {
return $this->__call(__FUNCTION__, [$params, $extra]); return $this->__call(__FUNCTION__, [$params, $extra]);
} }
/**
* Set loop callback (DEPRECATED).
*
* @param callable $callback Callback
*
* @return void
*/
public function setLoopCallback($callback): void
{
$this->API->setLoopCallback($callback);
}
/** /**
* Start MadelineProto's update handling loop, or run the provided async callable. * Start MadelineProto's update handling loop, or run the provided async callable.
* *
@ -5623,15 +5674,22 @@ class InternalDoc extends APIFactory
$this->API->stop(); $this->API->stop();
} }
/** /**
* Start MadelineProto's update handling loop in background, or run the provided async callable. * Restart update loop.
* *
* @param callable $callback Async callable to run * @return void
*
* @return mixed
*/ */
public function loopFork($callback = null): void public function restart(): void
{ {
$this->API->loopFork($callback); $this->API->restart();
}
/**
* Start MadelineProto's update handling loop in background.
*
* @return Promise
*/
public function loopFork(array $extra = [])
{
return $this->__call(__FUNCTION__, [$extra]);
} }
/** /**
* Close connection with client, connected via web. * Close connection with client, connected via web.

File diff suppressed because one or more lines are too long

View File

@ -79,7 +79,7 @@ trait Files
} elseif (\is_array($file)) { } elseif (\is_array($file)) {
return yield from $this->uploadFromTgfile($file, $cb, $encrypted); return yield from $this->uploadFromTgfile($file, $cb, $encrypted);
} }
if (\is_resource($file) || (is_object($file) && $file instanceof InputStream)) { if (\is_resource($file) || (\is_object($file) && $file instanceof InputStream)) {
return yield from $this->uploadFromStream($file, 0, '', $fileName, $cb, $encrypted); return yield from $this->uploadFromStream($file, 0, '', $fileName, $cb, $encrypted);
} }
if (!$this->settings['upload']['allow_automatic_upload']) { if (!$this->settings['upload']['allow_automatic_upload']) {
@ -212,7 +212,7 @@ trait Files
yield $stream->seek(0, \SEEK_END); yield $stream->seek(0, \SEEK_END);
$size = yield $stream->tell(); $size = yield $stream->tell();
yield $stream->seek(0); yield $stream->seek(0);
} else if (!$size) { } elseif (!$size) {
$this->logger->logger("No content length for stream, caching first"); $this->logger->logger("No content length for stream, caching first");
$body = $stream; $body = $stream;
$stream = new BlockingFile(\fopen('php://temp', 'r+b'), 'php://temp', 'r+b'); $stream = new BlockingFile(\fopen('php://temp', 'r+b'), 'php://temp', 'r+b');

View File

@ -240,7 +240,8 @@ trait BotAPI
if (isset($data['fwd_from']['channel_id'])) { if (isset($data['fwd_from']['channel_id'])) {
try { try {
$newd['forward_from_chat'] = yield from $this->getPwrChat(PeerHandler::toSupergroup($data['fwd_from']['channel_id'])); $newd['forward_from_chat'] = yield from $this->getPwrChat(PeerHandler::toSupergroup($data['fwd_from']['channel_id']));
} catch (\Throwable $e) {} } catch (\Throwable $e) {
}
} }
if (isset($data['fwd_from']['date'])) { if (isset($data['fwd_from']['date'])) {
$newd['forward_date'] = $data['fwd_from']['date']; $newd['forward_date'] = $data['fwd_from']['date'];

View File

@ -739,10 +739,10 @@ trait Tools
return $new; return $new;
} }
/** /**
* Inflate stripped photosize to full JPG payload * Inflate stripped photosize to full JPG payload.
* *
* @param string $stripped Stripped photosize * @param string $stripped Stripped photosize
* *
* @return string JPG payload * @return string JPG payload
*/ */
public static function inflateStripped(string $stripped): string public static function inflateStripped(string $stripped): string

View File

@ -19,7 +19,7 @@
namespace danog\MadelineProto\Wrappers; namespace danog\MadelineProto\Wrappers;
use EventHandler; use danog\MadelineProto\EventHandler;
/** /**
* Event handler. * Event handler.
@ -35,7 +35,7 @@ trait Events
/** /**
* Event handler instance. * Event handler instance.
* *
* @var \danog\MadelineProto\EventHandler * @var EventHandler
*/ */
private $event_handler_instance; private $event_handler_instance;
/** /**
@ -78,6 +78,7 @@ trait Events
$this->event_handler_methods[$method_name] = [$this->event_handler_instance, $method]; $this->event_handler_methods[$method_name] = [$this->event_handler_instance, $method];
} }
} }
$this->setReportPeers($this->event_handler_instance->getReportPeers());
$this->settings['updates']['callback'] = [$this, 'eventUpdateHandler']; $this->settings['updates']['callback'] = [$this, 'eventUpdateHandler'];
$this->settings['updates']['handle_updates'] = true; $this->settings['updates']['handle_updates'] = true;
$this->settings['updates']['run_callback'] = true; $this->settings['updates']['run_callback'] = true;
@ -86,12 +87,12 @@ trait Events
} }
} }
/** /**
* Unset event handler. * Unset event handler.
* *
* @param bool $disableUpdateHandling Whether to also disable internal update handling (will cause errors, otherwise will simply use the NOOP handler) * @param bool $disableUpdateHandling Whether to also disable internal update handling (will cause errors, otherwise will simply use the NOOP handler)
* *
* @return void * @return void
*/ */
public function unsetEventHandler(bool $disableUpdateHandling = false): void public function unsetEventHandler(bool $disableUpdateHandling = false): void
{ {
$this->event_handler = null; $this->event_handler = null;
@ -107,10 +108,19 @@ trait Events
* *
* @return EventHandler * @return EventHandler
*/ */
public function getEventHandler(): \danog\MadelineProto\EventHandler public function getEventHandler(): EventHandler
{ {
return $this->event_handler_instance; return $this->event_handler_instance;
} }
/**
* Check if an event handler instance is present.
*
* @return boolean
*/
public function hasEventHandler(): bool
{
return isset($this->event_handler_instance);
}
/** /**
* Event update handler. * Event update handler.
* *

View File

@ -35,17 +35,7 @@ trait Loop
* @var boolean * @var boolean
*/ */
private $stopLoop = false; private $stopLoop = false;
/**
* Set loop callback (DEPRECATED).
*
* @param callable $callback Callback
*
* @return void
*/
public function setLoopCallback($callback): void
{
$this->loop_callback = $callback;
}
/** /**
* Start MadelineProto's update handling loop, or run the provided async callable. * Start MadelineProto's update handling loop, or run the provided async callable.
* *
@ -75,7 +65,9 @@ trait Loop
if (!\is_callable($this->loop_callback) || \is_array($this->loop_callback) && $this->loop_callback[1] === 'onLoop' && !\method_exists(...$this->loop_callback)) { if (!\is_callable($this->loop_callback) || \is_array($this->loop_callback) && $this->loop_callback[1] === 'onLoop' && !\method_exists(...$this->loop_callback)) {
$this->loop_callback = null; $this->loop_callback = null;
} }
if (PHP_SAPI !== 'cli') {
static $inited = false;
if (PHP_SAPI !== 'cli' && !$inited) {
$needs_restart = true; $needs_restart = true;
try { try {
\set_time_limit(-1); \set_time_limit(-1);
@ -83,11 +75,11 @@ trait Loop
$needs_restart = true; $needs_restart = true;
} }
if (isset($_REQUEST['MadelineSelfRestart'])) { if (isset($_REQUEST['MadelineSelfRestart'])) {
$this->logger->logger("Self-restarted, restart token " . $_REQUEST['MadelineSelfRestart']); $this->logger->logger("Self-restarted, restart token ".$_REQUEST['MadelineSelfRestart']);
} }
$this->logger->logger($needs_restart ? 'Will self-restart' : 'Will not self-restart'); $this->logger->logger($needs_restart ? 'Will self-restart' : 'Will not self-restart');
$backtrace = \debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); $backtrace = \debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
$lockfile = \dirname(\end($backtrace)['file']) . '/bot' . $this->authorization['user']['id'] . '.lock'; $lockfile = \dirname(\end($backtrace)['file']).'/bot'.$this->authorization['user']['id'].'.lock';
unset($backtrace); unset($backtrace);
$try_locking = true; $try_locking = true;
if (!\file_exists($lockfile)) { if (!\file_exists($lockfile)) {
@ -120,7 +112,7 @@ trait Loop
if ($needs_restart) { if ($needs_restart) {
$logger =& $this->logger; $logger =& $this->logger;
Shutdown::addCallback(static function () use (&$logger) { Shutdown::addCallback(static function () use (&$logger) {
$address = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] ? 'tls' : 'tcp') . '://' . $_SERVER['SERVER_NAME']; $address = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] ? 'tls' : 'tcp').'://'.$_SERVER['SERVER_NAME'];
$port = $_SERVER['SERVER_PORT']; $port = $_SERVER['SERVER_PORT'];
$uri = $_SERVER['REQUEST_URI']; $uri = $_SERVER['REQUEST_URI'];
$params = $_GET; $params = $_GET;
@ -128,7 +120,7 @@ trait Loop
$url = \explode('?', $uri, 2)[0] ?? ''; $url = \explode('?', $uri, 2)[0] ?? '';
$query = \http_build_query($params); $query = \http_build_query($params);
$uri = \implode('?', [$url, $query]); $uri = \implode('?', [$url, $query]);
$payload = $_SERVER['REQUEST_METHOD'] . ' ' . $uri . ' ' . $_SERVER['SERVER_PROTOCOL'] . "\r\n" . 'Host: ' . $_SERVER['SERVER_NAME'] . "\r\n\r\n"; $payload = $_SERVER['REQUEST_METHOD'].' '.$uri.' '.$_SERVER['SERVER_PROTOCOL']."\r\n".'Host: '.$_SERVER['SERVER_NAME']."\r\n\r\n";
$logger->logger("Connecting to {$address}:{$port}"); $logger->logger("Connecting to {$address}:{$port}");
$a = \fsockopen($address, $port); $a = \fsockopen($address, $port);
$logger->logger("Sending self-restart payload"); $logger->logger("Sending self-restart payload");
@ -140,6 +132,7 @@ trait Loop
}, 'restarter'); }, 'restarter');
} }
$this->closeConnection('Bot was started'); $this->closeConnection('Bot was started');
$inited = true;
} }
if (!$this->settings['updates']['handle_updates']) { if (!$this->settings['updates']['handle_updates']) {
$this->settings['updates']['handle_updates'] = true; $this->settings['updates']['handle_updates'] = true;
@ -179,15 +172,22 @@ trait Loop
$this->signalUpdate(); $this->signalUpdate();
} }
/** /**
* Start MadelineProto's update handling loop in background, or run the provided async callable. * Restart update loop.
* *
* @param callable $callback Async callable to run * @return void
*
* @return mixed
*/ */
public function loopFork($callback = null): void public function restart(): void
{ {
Tools::callFork($this->loop($callback)); $this->stop();
}
/**
* Start MadelineProto's update handling loop in background.
*
* @return Promise
*/
public function loopFork(): Promise
{
return Tools::callFork($this->loop());
} }
/** /**
* Close connection with client, connected via web. * Close connection with client, connected via web.
@ -203,14 +203,14 @@ trait Loop
} }
$this->logger->logger($message); $this->logger->logger($message);
$buffer = @\ob_get_clean() ?: ''; $buffer = @\ob_get_clean() ?: '';
$buffer .= '<html><body><h1>' . \htmlentities($message) . '</h1></body></html>'; $buffer .= '<html><body><h1>'.\htmlentities($message).'</h1></body></html>';
\ignore_user_abort(true); \ignore_user_abort(true);
\header('Connection: close'); \header('Connection: close');
\header('Content-Type: text/html'); \header('Content-Type: text/html');
echo $buffer; echo $buffer;
\flush(); \flush();
$GLOBALS['exited'] = true; $GLOBALS['exited'] = true;
if (\function_exists(\fastcgi_finish_request::class)) { if (\function_exists('fastcgi_finish_request')) {
\fastcgi_finish_request(); \fastcgi_finish_request();
} }
} }