Skip to content

Commit b500f19

Browse files
committed
Add message chunking feature.
- Add `chunk($limit)` method. Closes #127
1 parent d35cfc0 commit b500f19

File tree

4 files changed

+111
-15
lines changed

4 files changed

+111
-15
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ All notable changes to `telegram` will be documented in this file
44

55
## Unreleased
66

7+
- Add message chunking feature (`chunk($limit)`) in cases where the message is too long. Closes [#127](https://github.com/laravel-notification-channels/telegram/issues/127).
8+
79
## 0.7.0 - 2021-10-28
810

911
- Dropped PHP 7.1 support. PR [#118](https://github.com/laravel-notification-channels/telegram/pull/118).

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -242,8 +242,9 @@ Notification::route('telegram', 'TELEGRAM_CHAT_ID')
242242

243243
- `to($chatId)`: (integer) Recipient's chat id.
244244
- `token($token)`: (string) Bot token if you wish to override the default token for a specific notification (optional).
245-
- `content('')`: (string) Notification message, supports markdown. For more information on supported markdown styles, check out these [docs](https://telegram-bot-sdk.readme.io/reference#section-formatting-options).
245+
- `content('', $limit = null)`: (string) Notification message, supports markdown. For more information on supported markdown styles, check out these [docs](https://telegram-bot-sdk.readme.io/reference#section-formatting-options).
246246
- `view($view, $data = [], $mergeData = [])`: (string) Blade template name with Telegram supported HTML or Markdown syntax content if you wish to use a view file instead of the `content()` method.
247+
- `chunk($limit = 4096)`: (integer) Message chars chunk size to send in parts (For long messages). Note: Chunked messages will be rate limited to one message per second to comply with rate limitation requirements from Telegram.
247248
- `button($text, $url)`: (string) Adds an inline "Call to Action" button. You can add as many as you want, and they'll be placed 2 in a row.
248249
- `buttonWithCallback($text, $callback_data)`: (string) Adds an inline button with callback. You can add as many as you want, and they'll be placed 2 in a row.
249250
- `disableNotification($disableNotification = true)`: (bool) Send the message silently. Users will receive a notification with no sound.

src/TelegramChannel.php

Lines changed: 68 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace NotificationChannels\Telegram;
44

5+
use Illuminate\Support\Collection;
56
use Illuminate\Contracts\Events\Dispatcher;
67
use Illuminate\Notifications\Events\NotificationFailed;
78
use Illuminate\Notifications\Notification;
@@ -25,8 +26,8 @@ class TelegramChannel
2526
/**
2627
* Channel constructor.
2728
*
28-
* @param Telegram $telegram
29-
* @param Dispatcher $dispatcher
29+
* @param Telegram $telegram
30+
* @param Dispatcher $dispatcher
3031
*/
3132
public function __construct(Telegram $telegram, Dispatcher $dispatcher)
3233
{
@@ -37,8 +38,8 @@ public function __construct(Telegram $telegram, Dispatcher $dispatcher)
3738
/**
3839
* Send the given notification.
3940
*
40-
* @param mixed $notifiable
41-
* @param Notification $notification
41+
* @param mixed $notifiable
42+
* @param Notification $notification
4243
*
4344
* @throws CouldNotSendNotification
4445
* @return null|array
@@ -55,7 +56,7 @@ public function send($notifiable, Notification $notification): ?array
5556
$to = $notifiable->routeNotificationFor('telegram', $notification)
5657
?? $notifiable->routeNotificationFor(self::class, $notification);
5758

58-
if (! $to) {
59+
if (!$to) {
5960
return null;
6061
}
6162

@@ -68,8 +69,41 @@ public function send($notifiable, Notification $notification): ?array
6869

6970
$params = $message->toArray();
7071

71-
try{
72+
try {
7273
if ($message instanceof TelegramMessage) {
74+
if ($message->shouldChunk()) {
75+
$replyMarkup = $message->getPayloadValue('reply_markup');
76+
77+
if ($replyMarkup) {
78+
unset($params['reply_markup']);
79+
}
80+
81+
$messages = $this->chunk($message->getPayloadValue('text'), $message->chunkSize);
82+
83+
$payloads = collect($messages)->filter()->map(function ($text) use ($params) {
84+
return array_merge($params, ['text' => $text]);
85+
});
86+
87+
if ($replyMarkup) {
88+
$lastMessage = $payloads->pop();
89+
$lastMessage['reply_markup'] = $replyMarkup;
90+
$payloads->push($lastMessage);
91+
}
92+
93+
return $payloads->map(function ($payload) {
94+
$response = $this->telegram->sendMessage($payload);
95+
96+
// To avoid rate limit of one message per second.
97+
sleep(1);
98+
99+
if ($response) {
100+
return json_decode($response->getBody()->getContents(), true);
101+
}
102+
103+
return $response;
104+
})->toArray();
105+
}
106+
73107
$response = $this->telegram->sendMessage($params);
74108
} elseif ($message instanceof TelegramLocation) {
75109
$response = $this->telegram->sendLocation($params);
@@ -89,7 +123,34 @@ public function send($notifiable, Notification $notification): ?array
89123
throw $exception;
90124
}
91125

92-
93126
return json_decode($response->getBody()->getContents(), true);
94127
}
128+
129+
/**
130+
* Chunk the given string into an array of strings.
131+
*
132+
* @param string $value
133+
* @param int $limit
134+
*
135+
* @return array
136+
*/
137+
public function chunk(string $value, int $limit = 4096): array
138+
{
139+
if (mb_strwidth($value, 'UTF-8') <= $limit) {
140+
return [$value];
141+
}
142+
143+
if ($limit >= 4097) {
144+
$limit = 4096;
145+
}
146+
147+
$output = explode("%#TGMSG#%", wordwrap($value, $limit, '%#TGMSG#%'));
148+
149+
// Fallback for when the string is too long and wordwrap doesn't cut it.
150+
if (count($output) <= 1) {
151+
$output = mb_str_split($value, $limit, 'UTF-8');
152+
}
153+
154+
return $output;
155+
}
95156
}

src/TelegramMessage.php

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,11 @@ class TelegramMessage implements JsonSerializable
1313
{
1414
use HasSharedLogic;
1515

16+
/** @var int Message Chunk Size */
17+
public $chunkSize;
18+
1619
/**
17-
* @param string $content
20+
* @param string $content
1821
*
1922
* @return self
2023
*/
@@ -26,7 +29,7 @@ public static function create(string $content = ''): self
2629
/**
2730
* Message constructor.
2831
*
29-
* @param string $content
32+
* @param string $content
3033
*/
3134
public function __construct(string $content = '')
3235
{
@@ -37,29 +40,58 @@ public function __construct(string $content = '')
3740
/**
3841
* Notification message (Supports Markdown).
3942
*
40-
* @param string $content
43+
* @param string $content
44+
* @param int|null $limit
4145
*
4246
* @return $this
4347
*/
44-
public function content(string $content): self
48+
public function content(string $content, int $limit = null): self
4549
{
4650
$this->payload['text'] = $content;
4751

52+
if ($limit) {
53+
$this->chunkSize = $limit;
54+
}
55+
4856
return $this;
4957
}
5058

5159
/**
5260
* Attach a view file as the content for the notification.
5361
* Supports Laravel blade template.
5462
*
55-
* @param string $view
56-
* @param array $data
57-
* @param array $mergeData
63+
* @param string $view
64+
* @param array $data
65+
* @param array $mergeData
5866
*
5967
* @return $this
6068
*/
6169
public function view(string $view, array $data = [], array $mergeData = []): self
6270
{
6371
return $this->content(View::make($view, $data, $mergeData)->render());
6472
}
73+
74+
/**
75+
* Chunk message to given size.
76+
*
77+
* @param int $limit
78+
*
79+
* @return $this
80+
*/
81+
public function chunk(int $limit = 4096): self
82+
{
83+
$this->chunkSize = $limit;
84+
85+
return $this;
86+
}
87+
88+
/**
89+
* Should the message be chunked.
90+
*
91+
* @return bool
92+
*/
93+
public function shouldChunk(): bool
94+
{
95+
return null !== $this->chunkSize;
96+
}
6597
}

0 commit comments

Comments
 (0)