From 15ded98c927104f472565d67031949e097f979e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Dumas?= Date: Fri, 31 Jan 2025 14:55:45 +0100 Subject: [PATCH] [Notifier] [Bluesky] Allow to attach website preview card --- .../Bridge/Bluesky/BlueskyOptions.php | 12 ++++++ .../Bridge/Bluesky/BlueskyTransport.php | 20 +++++++++ .../Notifier/Bridge/Bluesky/CHANGELOG.md | 5 +++ .../Notifier/Bridge/Bluesky/README.md | 24 +++++++++++ .../Bluesky/Tests/BlueskyTransportTest.php | 43 ++++++++++++++++--- 5 files changed, 97 insertions(+), 7 deletions(-) diff --git a/src/Symfony/Component/Notifier/Bridge/Bluesky/BlueskyOptions.php b/src/Symfony/Component/Notifier/Bridge/Bluesky/BlueskyOptions.php index fe576986164e4..71066ad5c90b4 100644 --- a/src/Symfony/Component/Notifier/Bridge/Bluesky/BlueskyOptions.php +++ b/src/Symfony/Component/Notifier/Bridge/Bluesky/BlueskyOptions.php @@ -43,4 +43,16 @@ public function attachMedia(File $file, string $description = ''): static return $this; } + + public function attachCard(string $uri, File $thumb, string $title = '', string $description = ''): static + { + $this->options['external'] = [ + 'uri' => $uri, + 'thumb' => $thumb, + 'title' => $title, + 'description' => $description, + ]; + + return $this; + } } diff --git a/src/Symfony/Component/Notifier/Bridge/Bluesky/BlueskyTransport.php b/src/Symfony/Component/Notifier/Bridge/Bluesky/BlueskyTransport.php index 3233159a28a0a..d8f892a6186f9 100644 --- a/src/Symfony/Component/Notifier/Bridge/Bluesky/BlueskyTransport.php +++ b/src/Symfony/Component/Notifier/Bridge/Bluesky/BlueskyTransport.php @@ -91,6 +91,26 @@ protected function doSend(MessageInterface $message): SentMessage unset($options['attach']); } + if (isset($options['external'])) { + $uploadedMedia = $this->uploadMedia([ + [ + 'file' => $options['external']['thumb'], + 'description' => $options['external']['description'], + ], + ]); + + $options['record']['embed'] = [ + '$type' => 'app.bsky.embed.external', + 'external' => [ + 'uri' => $options['external']['uri'], + 'title' => $options['external']['title'], + 'description' => $options['external']['description'], + 'thumb' => $uploadedMedia[array_key_first($uploadedMedia)]['image'], + ], + ]; + unset($options['external']); + } + $response = $this->client->request('POST', \sprintf('https://%s/xrpc/com.atproto.repo.createRecord', $this->getEndpoint()), [ 'auth_bearer' => $this->authSession['accessJwt'] ?? null, 'json' => $options, diff --git a/src/Symfony/Component/Notifier/Bridge/Bluesky/CHANGELOG.md b/src/Symfony/Component/Notifier/Bridge/Bluesky/CHANGELOG.md index d337db00df015..ded2a5fc4cd0e 100644 --- a/src/Symfony/Component/Notifier/Bridge/Bluesky/CHANGELOG.md +++ b/src/Symfony/Component/Notifier/Bridge/Bluesky/CHANGELOG.md @@ -1,6 +1,11 @@ CHANGELOG ========= +7.3 +--- + + * Add option to attach a website preview card + 7.2 --- diff --git a/src/Symfony/Component/Notifier/Bridge/Bluesky/README.md b/src/Symfony/Component/Notifier/Bridge/Bluesky/README.md index 72f5bb9000f58..850a16270ff8e 100644 --- a/src/Symfony/Component/Notifier/Bridge/Bluesky/README.md +++ b/src/Symfony/Component/Notifier/Bridge/Bluesky/README.md @@ -10,6 +10,30 @@ DSN example BLUESKY_DSN=bluesky://nyholm.bsky.social:p4ssw0rd@bsky.social ``` +Adding Options to a Message +--------------------------- + +Use a `BlueskyOptions` object to add options to the message: + +```php +use Symfony\Component\Notifier\Bridge\Bluesky\BlueskyOptions; +use Symfony\Component\Notifier\Message\ChatMessage; + +$message = new ChatMessage('My message'); + +// Add website preview card to the message +$options = (new BlueskyOptions()) + ->attachCard('https://example.com', new File('image.jpg')) + // You can also add media to the message + //->attachMedia(new File($command->fileName), 'description') + ; + +// Add the custom options to the Bluesky message and send the message +$message->options($options); + +$chatter->send($message); +``` + Resources --------- diff --git a/src/Symfony/Component/Notifier/Bridge/Bluesky/Tests/BlueskyTransportTest.php b/src/Symfony/Component/Notifier/Bridge/Bluesky/Tests/BlueskyTransportTest.php index 1cfa099e04537..de1e0b2dc5b3e 100644 --- a/src/Symfony/Component/Notifier/Bridge/Bluesky/Tests/BlueskyTransportTest.php +++ b/src/Symfony/Component/Notifier/Bridge/Bluesky/Tests/BlueskyTransportTest.php @@ -274,9 +274,12 @@ public function testParseFacetsUrlWithTrickyRegex() $this->assertEquals($expected, $this->parseFacets($input)); } - public function testWithMedia() + /** + * @dataProvider sendMessageWithEmbedDataProvider + */ + public function testWithEmbed(BlueskyOptions $blueskyOptions, string $expectedJsonResponse) { - $transport = $this->createTransport(new MockHttpClient((function () { + $transport = $this->createTransport(new MockHttpClient((function () use ($expectedJsonResponse) { yield function (string $method, string $url, array $options) { $this->assertSame('POST', $method); $this->assertSame('https://bsky.social/xrpc/com.atproto.server.createSession', $url); @@ -299,23 +302,49 @@ public function testWithMedia() ]]); }; - yield function (string $method, string $url, array $options) { + yield function (string $method, string $url, array $options) use ($expectedJsonResponse) { $this->assertSame('POST', $method); $this->assertSame('https://bsky.social/xrpc/com.atproto.repo.createRecord', $url); $this->assertArrayHasKey('authorization', $options['normalized_headers']); - $this->assertSame('{"repo":null,"collection":"app.bsky.feed.post","record":{"$type":"app.bsky.feed.post","text":"Hello World!","createdAt":"2024-04-28T08:40:17.000000Z","embed":{"$type":"app.bsky.embed.images","images":[{"alt":"A fixture","image":{"$type":"blob","ref":{"$link":"bafkreibabalobzn6cd366ukcsjycp4yymjymgfxcv6xczmlgpemzkz3cfa"},"mimeType":"image\/png","size":760898}}]}}}', $options['body']); + $this->assertSame($expectedJsonResponse, $options['body']); return new JsonMockResponse(['cid' => '103254962155278888']); }; })())); - $options = (new BlueskyOptions()) - ->attachMedia(new File(__DIR__.'/fixtures.gif'), 'A fixture'); - $result = $transport->send(new ChatMessage('Hello World!', $options)); + $result = $transport->send(new ChatMessage('Hello World!', $blueskyOptions)); $this->assertSame('103254962155278888', $result->getMessageId()); } + public function sendMessageWithEmbedDataProvider(): iterable + { + yield 'With media' => [ + 'options' => (new BlueskyOptions())->attachMedia(new File(__DIR__.'/fixtures.gif'), 'A fixture'), + 'expectedResponse' => '{"repo":null,"collection":"app.bsky.feed.post","record":{"$type":"app.bsky.feed.post","text":"Hello World!","createdAt":"2024-04-28T08:40:17.000000Z","embed":{"$type":"app.bsky.embed.images","images":[{"alt":"A fixture","image":{"$type":"blob","ref":{"$link":"bafkreibabalobzn6cd366ukcsjycp4yymjymgfxcv6xczmlgpemzkz3cfa"},"mimeType":"image\/png","size":760898}}]}}}', + ]; + + yield 'With website preview card and all optionnal informations' => [ + 'options' => (new BlueskyOptions()) + ->attachCard( + 'https://example.com', + new File(__DIR__.'/fixtures.gif'), + 'Fork me im famous', + 'Click here to go to website!' + ), + 'expectedResponse' => '{"repo":null,"collection":"app.bsky.feed.post","record":{"$type":"app.bsky.feed.post","text":"Hello World!","createdAt":"2024-04-28T08:40:17.000000Z","embed":{"$type":"app.bsky.embed.external","external":{"uri":"https:\/\/example.com","title":"Fork me im famous","description":"Click here to go to website!","thumb":{"$type":"blob","ref":{"$link":"bafkreibabalobzn6cd366ukcsjycp4yymjymgfxcv6xczmlgpemzkz3cfa"},"mimeType":"image\/png","size":760898}}}}}', + ]; + + yield 'With website preview card and minimal information' => [ + 'options' => (new BlueskyOptions()) + ->attachCard( + 'https://example.com', + new File(__DIR__.'/fixtures.gif') + ), + 'expectedResponse' => '{"repo":null,"collection":"app.bsky.feed.post","record":{"$type":"app.bsky.feed.post","text":"Hello World!","createdAt":"2024-04-28T08:40:17.000000Z","embed":{"$type":"app.bsky.embed.external","external":{"uri":"https:\/\/example.com","title":"","description":"","thumb":{"$type":"blob","ref":{"$link":"bafkreibabalobzn6cd366ukcsjycp4yymjymgfxcv6xczmlgpemzkz3cfa"},"mimeType":"image\/png","size":760898}}}}}', + ]; + } + /** * A small helper function to test BlueskyTransport::parseFacets(). */