8000 [Notifier] Telegram Bridge support sending local photo · symfony/symfony@078c582 · GitHub
[go: up one dir, main page]

Skip to content

Commit 078c582

Browse files
committed
[Notifier] Telegram Bridge support sending local photo
1 parent 7d310a3 commit 078c582

File tree

5 files changed

+156
-1
lines changed

5 files changed

+156
-1
lines changed

src/Symfony/Component/Notifier/Bridge/Telegram/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
CHANGELOG
22
=========
33

4+
6.4
5+
---
6+
7+
* Add support for using local files in `sendPhoto` API method
8+
49
6.3
510
---
611

src/Symfony/Component/Notifier/Bridge/Telegram/README.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,34 @@ $chatMessage->options($telegramOptions);
7676
$chatter->send($chatMessage);
7777
```
7878

79+
Adding Local Photo to a Message
80+
-------------------------------
81+
With a Telegram message, you can use the `TelegramOptions` class to add
82+
[message options](https://core.telegram.org/bots/api).
83+
84+
```php
85+
use Symfony\Component\Notifier\Bridge\Telegram\Reply\Markup\Button\InlineKeyboardButton;
86+
use Symfony\Component\Notifier\Bridge\Telegram\Reply\Markup\InlineKeyboardMarkup;
87+
use Symfony\Component\Notifier\Bridge\Telegram\TelegramOptions;
88+
use Symfony\Component\Notifier\Message\ChatMessage;
89+
90+
$chatMessage = new ChatMessage('Photo Caption');
91+
92+
// Create Telegram options
93+
$telegramOptions = (new TelegramOptions())
94+
->chatId('@symfonynotifierdev')
95+
->parseMode('MarkdownV2')
96+
->disableWebPagePreview(true)
97+
->hasSpoiler(true)
98+
->protectContent(true)
99+
->photo('files/android-chrome-192x192.png');
100+
101+
// Add the custom options to the chat message and send the message
102+
$chatMessage->options($telegramOptions);
103+
104+
$chatter->send($chatMessage);
105+
```
106+
79107
Updating Messages
80108
-----------------
81109

src/Symfony/Component/Notifier/Bridge/Telegram/TelegramTransport.php

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ protected function doSend(MessageInterface $message): SentMessage
6969
}
7070

7171
$options = $message->getOptions()?->toArray() ?? [];
72+
$optionsContainer = 'json';
7273
$options['chat_id'] ??= $message->getRecipientId() ?: $this->chatChannel;
7374
$options['text'] = $message->getSubject();
7475

@@ -80,12 +81,16 @@ protected function doSend(MessageInterface $message): SentMessage
8081
if (isset($options['photo'])) {
8182
$options['caption'] = $options['text'];
8283
unset($options['text']);
84+
if ($this->isPhotoPrivate($options['photo'])) {
85+
$options['photo'] = fopen($options['photo'], 'rb');
86+
$optionsContainer = 'body';
87+
}
8388
}
8489

8590
$endpoint = sprintf('https://%s/bot%s/%s', $this->getEndpoint(), $this->token, $this->getPath($options));
8691

8792
$response = $this->client->request('POST', $endpoint, [
88-
'json' => array_filter($options),
93+
$optionsContainer => array_filter($options),
8994
]);
9095

9196
try {
@@ -128,4 +133,12 @@ private function getAction(array $options): string
128133
default => 'post',
129134
};
130135
}
136+
137+
private function isPhotoPrivate(string $photo): bool
138+
{
139+
return match (parse_url($photo, PHP_URL_SCHEME)) {
140+
'http', 'https' => false,
141+
default => true,
142+
};
143+
}
131144
}

src/Symfony/Component/Notifier/Bridge/Telegram/Tests/TelegramTransportTest.php

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -411,4 +411,113 @@ public function testSendPhotoWithOptions()
411411
$this->assertEquals(1, $sentMessage->getMessageId());
412412
$this->assertEquals('telegram://api.telegram.org?channel=testChannel', $sentMessage->getTransport());
413413
}
414+
415+
public function testSendLocalPhotoWithOptions()
416+
{
417+
$response = $this->createMock(ResponseInterface::class);
418+
$response->expects($this->exactly(2))
419+
->method('getStatusCode')
420+
->willReturn(200);
421+
422+
$content = <<<JSON
423+
{
424+
"ok": true,
425+
"result": {
426+
"message_id": 1,
427+
"from": {
428+
"id": 12345678,
429+
"is_bot": true,
430+
"first_name": "YourBot",
431+
"username": "YourBot"
432+
},
433+
"chat": {
434+
"id": 1234567890,
435+
"first_name": "John",
436+
"last_name": "Doe",
437+
"username": "JohnDoe",
438+
"type": "private"
439+
},
440+
"date": 1459958199,
441+
"photo": [
442+
{
443+
"file_id": "ABCDEF",
444+
"file_unique_id" : "ABCDEF1",
445+
"file_size": 1378,
446+
"width": 90,
447+
"height": 51
448+
},
449+
{
450+
"file_id": "ABCDEF",
451+
"file_unique_id" : "ABCDEF2",
452+
"file_size": 19987,
453+
"width": 320,
454+
"height": 180
455+
}
456+
],
457+
"caption": "Hello from Bot!"
458+
}
459+
}
460+
JSON;
461+
462+
$response->expects($this->once())
463+
->method('getContent')
464+
->willReturn($content)
465+
;
466+
467+
468+
$client = new MockHttpClient(function (string $method, string $url, array $options = []) use ($response): ResponseInterface {
469+
$this->assertStringEndsWith('/sendPhoto', $url);
470+
$this->assertSame(1, preg_match('/^Content-Type: multipart\/form-data; boundary=(?<boundary>.+)$/', $options['normalized_headers']['content-type'][0], $matches));
471+
472+
$this->assertSame('Content-Length: 576', $options['normalized_headers']['content-length'][0]);
473+
$expectedBody = <<<BODY
474+
--{$matches['boundary']}
475+
Content-Disposition: form-data; name="photo"; filename="fixtures.png"
476+
Content-Type: image/png
477+
478+
%s
479+
--{$matches['boundary']}
480+
Content-Disposition: form-data; name="has_spoiler"
481+
482+
1
483+
--{$matches['boundary']}
484+
Content-Disposition: form-data; name="chat_id"
485+
486+
testChannel
487+
--{$matches['boundary']}
488+
Content-Disposition: form-data; name="parse_mode"
489+
490+
MarkdownV2
491+
--{$matches['boundary']}
492+
Content-Disposition: form-data; name="caption"
493+
494+
testMessage
495+
--{$matches['boundary']}--
496+
497+
BODY;
498+
$expectedBody = str_replace("\n", "\r\n", $expectedBody);
499+
$expectedBody = sprintf($expectedBody, file_get_contents(__DIR__.'/fixtures.png'));
500+
501+
$body = '';
502+
do {
503+
$body .= $chunk = $options['body']();
504+
} while (strlen($chunk) > 0);
505+
$this->assertSame($expectedBody, $body);
506+
507+
return $response;
508+
});
509+
510+
$transport = self::createTransport($client, 'testChannel');
511+
512+
$messageOptions = new TelegramOptions();
513+
$messageOptions
514+
->photo(__DIR__.'/fixtures.png')
515+
->hasSpoiler(true)
516+
;
517+
518+
$sentMessage = $transport->send(new ChatMessage('testMessage', $messageOptions));
519+
520+
$this->assertEquals(1, $sentMessage->getMessageId());
521+
$this->assertEquals('telegram://api.telegram.org?channel=testChannel', $sentMessage->getTransport());
522+
}
414523
}
Loading

0 commit comments

Comments
 (0)
0