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
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy