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(). */
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: