diff --git a/src/Monolog/Handler/TelegramBotHandler.php b/src/Monolog/Handler/TelegramBotHandler.php index 9ea05737..e8c818fe 100644 --- a/src/Monolog/Handler/TelegramBotHandler.php +++ b/src/Monolog/Handler/TelegramBotHandler.php @@ -43,6 +43,11 @@ class TelegramBotHandler extends AbstractProcessingHandler 'Markdown', // legacy mode without underline and strikethrough, use MarkdownV2 instead ]; + /** + * The maximum number of characters allowed in a message according to the Telegram api documentation + */ + private const MAX_MESSAGE_LENGTH = 4096; + /** * Telegram bot access token provided by BotFather. * Create telegram bot with https://telegram.me/BotFather and use access token from it. @@ -78,18 +83,37 @@ class TelegramBotHandler extends AbstractProcessingHandler private $disableNotification; /** - * @param string $apiKey Telegram bot access token provided by BotFather + * True - split a message longer than MAX_MESSAGE_LENGTH into parts and send in multiple messages. + * False - truncates a message that is too long. + * @var bool + */ + private $splitLongMessages; + + /** + * Adds 1-second delay between sending a split message (according to Telegram API to avoid 429 Too Many Requests). + * @var bool + */ + private $delayBetweenMessages; + + /** + * @param string $apiKey Telegram bot access token provided by BotFather * @param string $channel Telegram channel name + * @param bool $splitLongMessages Split a message longer than MAX_MESSAGE_LENGTH into parts and send in multiple messages + * @param bool $delayBetweenMessages Adds delay between sending a split message according to Telegram API + * @throws MissingExtensionException */ public function __construct( string $apiKey, string $channel, - $level = Logger::DEBUG, - bool $bubble = true, + $level = Logger::DEBUG, + bool $bubble = true, string $parseMode = null, - bool $disableWebPagePreview = null, - bool $disableNotification = null - ) { + bool $disableWebPagePreview = null, + bool $disableNotification = null, + bool $splitLongMessages = false, + bool $delayBetweenMessages = false + ) + { if (!extension_loaded('curl')) { throw new MissingExtensionException('The curl extension is needed to use the TelegramBotHandler'); } @@ -101,6 +125,8 @@ class TelegramBotHandler extends AbstractProcessingHandler $this->setParseMode($parseMode); $this->disableWebPagePreview($disableWebPagePreview); $this->disableNotification($disableNotification); + $this->splitLongMessages($splitLongMessages); + $this->delayBetweenMessages($delayBetweenMessages); } public function setParseMode(string $parseMode = null): self @@ -128,6 +154,31 @@ class TelegramBotHandler extends AbstractProcessingHandler return $this; } + /** + * True - split a message longer than MAX_MESSAGE_LENGTH into parts and send in multiple messages. + * False - truncates a message that is too long. + * @param bool $splitLongMessages + * @return $this + */ + public function splitLongMessages(bool $splitLongMessages = false): self + { + $this->splitLongMessages = $splitLongMessages; + + return $this; + } + + /** + * Adds 1-second delay between sending a split message (according to Telegram API to avoid 429 Too Many Requests). + * @param bool $delayBetweenMessages + * @return $this + */ + public function delayBetweenMessages(bool $delayBetweenMessages = false): self + { + $this->delayBetweenMessages = $delayBetweenMessages; + + return $this; + } + /** * {@inheritDoc} */ @@ -150,7 +201,7 @@ class TelegramBotHandler extends AbstractProcessingHandler } if (!empty($messages)) { - $this->send((string) $this->getFormatter()->formatBatch($messages)); + $this->send((string)$this->getFormatter()->formatBatch($messages)); } } @@ -167,6 +218,19 @@ class TelegramBotHandler extends AbstractProcessingHandler * @param string $message */ protected function send(string $message): void + { + $messages = $this->handleMessageLength($message); + + foreach ($messages as $key => $msg) { + if ($this->delayBetweenMessages && $key > 0) { + sleep(1); + } + + $this->sendCurl($msg); + } + } + + protected function sendCurl(string $message): void { $ch = curl_init(); $url = self::BOT_API . $this->apiKey . '/SendMessage'; @@ -191,4 +255,19 @@ class TelegramBotHandler extends AbstractProcessingHandler throw new RuntimeException('Telegram API error. Description: ' . $result['description']); } } + + /** + * Handle a message that is too long: truncates or splits into several + * @param string $message + * @return string[] + */ + private function handleMessageLength(string $message): array + { + $truncatedMarker = ' (...truncated)'; + if (!$this->splitLongMessages && strlen($message) > self::MAX_MESSAGE_LENGTH) { + return [substr($message, 0, self::MAX_MESSAGE_LENGTH - strlen($truncatedMarker)) . $truncatedMarker]; + } + + return str_split($message, self::MAX_MESSAGE_LENGTH); + } }