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

Skip to content

Commit 08a89a1

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

File tree

5 files changed

+155
-2
lines changed

5 files changed

+155
-2
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 & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111

1212
namespace Symfony\Component\Notifier\Bridge\Telegram;
1313

14-
use Symfony\Component\Notifier\Exception\LogicException;
1514
use Symfony\Component\Notifier\Exception\TransportException;
1615
use Symfony\Component\Notifier\Exception\UnsupportedMessageTypeException;
1716
use Symfony\Component\Notifier\Message\ChatMessage;
@@ -69,6 +68,7 @@ protected function doSend(MessageInterface $message): SentMessage
6968
}
7069

7170
$options = $message->getOptions()?->toArray() ?? [];
71+
$optionsContainer = 'json';
7272
$options['chat_id'] ??= $message->getRecipientId() ?: $this->chatChannel;
7373
$options['text'] = $message->getSubject();
7474

@@ -80,12 +80,16 @@ protected function doSend(MessageInterface $message): SentMessage
8080
if (isset($options['photo'])) {
8181
$options['caption'] = $options['text'];
8282
unset($options['text']);
83+
if ($this->isPhotoPrivate($options['photo'])) {
84+
$options['photo'] = fopen($options['photo'], 'r');
85+
$optionsContainer = 'body';
86+
}
8387
}
8488

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

8791
$response = $this->client->request('POST', $endpoint, [
88-
'json' => array_filter($options),
92+
$optionsContainer => array_filter($options),
8993
]);
9094

9195
try {
@@ -128,4 +132,12 @@ private function getAction(array $options): string
128132
default => 'post',
129133
};
130134
}
135+
136+
private function isPhotoPrivate(string $photo): bool
137+
{
138+
return match (parse_url($photo, \PHP_URL_SCHEME)) {
139+
'http', 'https' => false,
140+
default => true,
141+
};
142+
}
131143
}

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

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -411,4 +411,112 @@ 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+
$client = new MockHttpClient(function (string $method, string $url, array $options = []) use ($response): ResponseInterface {
468+
$this->assertStringEndsWith('/sendPhoto', $url);
469+
$this->assertSame(1, preg_match('/^Content-Type: multipart\/form-data; boundary=(?<boundary>.+)$/', $options['normalized_headers']['content-type'][0], $matches));
470+
471+
$this->assertSame('Content-Length: 576', $options['normalized_headers']['content-length'][0]);
472+
$expectedBody = <<<BODY
473+
--{$matches['boundary']}
474+
Content-Disposition: form-data; name="photo"; filename="fixtures.png"
475+
Content-Type: image/png
476+
477+
%s
478+
--{$matches['boundary']}
479+
Content-Disposition: form-data; name="has_spoiler"
480+
481+
1
482+
--{$matches['boundary']}
483+
Content-Disposition: form-data; name="chat_id"
484+
485+
testChannel
486+
--{$matches['boundary']}
487+
Content-Disposition: form-data; name="parse_mode"
488+
489+
MarkdownV2
490+
--{$matches['boundary']}
491+
Content-Disposition: form-data; name="caption"
492+
493+
testMessage
494+
--{$matches['boundary']}--
495+
496+
BODY;
497+
$expectedBody = str_replace("\n", "\r\n", $expectedBody);
498+
$expectedBody = sprintf($expectedBody, file_get_contents(__DIR__.'/fixtures.png'));
499+
500+
$body = '';
501+
do {
502+
$body .= $chunk = $options['body']();
503+
} while ('' !== $chunk);
504+
$this->assertSame($expectedBody, $body);
505+
506+
return $response;
507+
});
508+
509+
$transport = self::createTransport($client, 'testChannel');
510+
511+
$messageOptions = new TelegramOptions();
512+
$messageOptions
513+
->photo(__DIR__.'/fixtures.png')
514+
->hasSpoiler(true)
515+
;
516+
517+
$sentMessage = $transport->send(new ChatMessage('testMessage', $messageOptions));
518+
519+
$this->assertEquals(1, $sentMessage->getMessageId());
520+
$this->assertEquals('telegram://api.telegram.org?channel=testChannel', $sentMessage->getTransport());
521+
}
414522
}
Loading

0 commit comments

Comments
 (0)
0