├── CHANGELOG.md ├── DiscordOptions.php ├── DiscordTransport.php ├── DiscordTransportFactory.php ├── Embeds ├── AbstractDiscordEmbed.php ├── AbstractDiscordEmbedObject.php ├── DiscordAuthorEmbedObject.php ├── DiscordEmbed.php ├── DiscordEmbedInterface.php ├── DiscordEmbedObjectInterface.php ├── DiscordFieldEmbedObject.php ├── DiscordFooterEmbedObject.php └── DiscordMediaEmbedObject.php ├── LICENSE ├── README.md └── composer.json /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | CHANGELOG 2 | ========= 3 | 4 | 6.2 5 | --- 6 | 7 | * Check embed limitations 8 | 9 | 5.3 10 | --- 11 | 12 | * The bridge is not marked as `@experimental` anymore 13 | 14 | 5.2.0 15 | ----- 16 | 17 | * Added the bridge 18 | -------------------------------------------------------------------------------- /DiscordOptions.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Symfony\Component\Notifier\Bridge\Discord; 13 | 14 | use Symfony\Component\Notifier\Bridge\Discord\Embeds\DiscordEmbedInterface; 15 | use Symfony\Component\Notifier\Exception\LogicException; 16 | use Symfony\Component\Notifier\Message\MessageOptionsInterface; 17 | 18 | /** 19 | * @author Karoly Gossler 20 | */ 21 | final class DiscordOptions implements MessageOptionsInterface 22 | { 23 | public function __construct( 24 | private array $options = [], 25 | ) { 26 | } 27 | 28 | public function toArray(): array 29 | { 30 | return $this->options; 31 | } 32 | 33 | public function getRecipientId(): string 34 | { 35 | return ''; 36 | } 37 | 38 | /** 39 | * @return $this 40 | */ 41 | public function username(string $username): static 42 | { 43 | $this->options['username'] = $username; 44 | 45 | return $this; 46 | } 47 | 48 | /** 49 | * @return $this 50 | */ 51 | public function avatarUrl(string $avatarUrl): static 52 | { 53 | $this->options['avatar_url'] = $avatarUrl; 54 | 55 | return $this; 56 | } 57 | 58 | /** 59 | * @return $this 60 | */ 61 | public function tts(bool $tts): static 62 | { 63 | $this->options['tts'] = $tts; 64 | 65 | return $this; 66 | } 67 | 68 | /** 69 | * @return $this 70 | */ 71 | public function addEmbed(DiscordEmbedInterface $embed): static 72 | { 73 | if (!isset($this->options['embeds'])) { 74 | $this->options['embeds'] = []; 75 | } 76 | 77 | if (\count($this->options['embeds']) >= 10) { 78 | throw new LogicException(\sprintf('The "%s" only supports max 10 embeds.', __CLASS__)); 79 | } 80 | 81 | $this->options['embeds'][] = $embed->toArray(); 82 | 83 | return $this; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /DiscordTransport.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Symfony\Component\Notifier\Bridge\Discord; 13 | 14 | use Symfony\Component\Notifier\Exception\LengthException; 15 | use Symfony\Component\Notifier\Exception\TransportException; 16 | use Symfony\Component\Notifier\Exception\UnsupportedMessageTypeException; 17 | use Symfony\Component\Notifier\Message\ChatMessage; 18 | use Symfony\Component\Notifier\Message\MessageInterface; 19 | use Symfony\Component\Notifier\Message\SentMessage; 20 | use Symfony\Component\Notifier\Transport\AbstractTransport; 21 | use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; 22 | use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; 23 | use Symfony\Contracts\HttpClient\HttpClientInterface; 24 | 25 | /** 26 | * @author Mathieu Piot 27 | */ 28 | final class DiscordTransport extends AbstractTransport 29 | { 30 | protected const HOST = 'discord.com'; 31 | 32 | private const SUBJECT_LIMIT = 2000; 33 | 34 | public function __construct( 35 | #[\SensitiveParameter] private string $token, 36 | private string $webhookId, 37 | ?HttpClientInterface $client = null, 38 | ?EventDispatcherInterface $dispatcher = null, 39 | ) { 40 | parent::__construct($client, $dispatcher); 41 | } 42 | 43 | public function __toString(): string 44 | { 45 | return \sprintf('discord://%s?webhook_id=%s', $this->getEndpoint(), $this->webhookId); 46 | } 47 | 48 | public function supports(MessageInterface $message): bool 49 | { 50 | return $message instanceof ChatMessage && (null === $message->getOptions() || $message->getOptions() instanceof DiscordOptions); 51 | } 52 | 53 | /** 54 | * @see https://discord.com/developers/docs/resources/webhook 55 | */ 56 | protected function doSend(MessageInterface $message): SentMessage 57 | { 58 | if (!$message instanceof ChatMessage) { 59 | throw new UnsupportedMessageTypeException(__CLASS__, ChatMessage::class, $message); 60 | } 61 | 62 | $options = $message->getOptions()?->toArray() ?? []; 63 | $options['content'] = $message->getSubject(); 64 | 65 | if (mb_strlen($options['content'], 'UTF-8') > self::SUBJECT_LIMIT) { 66 | throw new LengthException(\sprintf('The subject length of a Discord message must not exceed %d characters.', self::SUBJECT_LIMIT)); 67 | } 68 | 69 | $endpoint = \sprintf('https://%s/api/webhooks/%s/%s', $this->getEndpoint(), $this->webhookId, $this->token); 70 | $response = $this->client->request('POST', $endpoint, [ 71 | 'json' => array_filter($options), 72 | ]); 73 | 74 | try { 75 | $statusCode = $response->getStatusCode(); 76 | } catch (TransportExceptionInterface $e) { 77 | throw new TransportException('Could not reach the remote Discord server.', $response, 0, $e); 78 | } 79 | 80 | if (204 !== $statusCode) { 81 | $result = $response->toArray(false); 82 | 83 | if (401 === $statusCode) { 84 | $originalContent = $message->getSubject(); 85 | $errorMessage = $result['message']; 86 | $errorCode = $result['code']; 87 | throw new TransportException(\sprintf('Unable to post the Discord message: "%s" (%d: "%s").', $originalContent, $errorCode, $errorMessage), $response); 88 | } 89 | 90 | if (400 === $statusCode) { 91 | $originalContent = $message->getSubject(); 92 | 93 | $errorMessage = ''; 94 | foreach ($result as $fieldName => $message) { 95 | $message = \is_array($message) ? implode(' ', $message) : $message; 96 | $errorMessage .= $fieldName.': '.$message.' '; 97 | } 98 | 99 | $errorMessage = trim($errorMessage); 100 | throw new TransportException(\sprintf('Unable to post the Discord message: "%s" (%s).', $originalContent, $errorMessage), $response); 101 | } 102 | 103 | throw new TransportException(\sprintf('Unable to post the Discord message: "%s" (Status Code: %d).', $message->getSubject(), $statusCode), $response); 104 | } 105 | 106 | return new SentMessage($message, (string) $this); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /DiscordTransportFactory.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Symfony\Component\Notifier\Bridge\Discord; 13 | 14 | use Symfony\Component\Notifier\Exception\UnsupportedSchemeException; 15 | use Symfony\Component\Notifier\Transport\AbstractTransportFactory; 16 | use Symfony\Component\Notifier\Transport\Dsn; 17 | 18 | /** 19 | * @author Mathieu Piot 20 | */ 21 | final class DiscordTransportFactory extends AbstractTransportFactory 22 | { 23 | public function create(Dsn $dsn): DiscordTransport 24 | { 25 | $scheme = $dsn->getScheme(); 26 | 27 | if ('discord' !== $scheme) { 28 | throw new UnsupportedSchemeException($dsn, 'discord', $this->getSupportedSchemes()); 29 | } 30 | 31 | $token = $this->getUser($dsn); 32 | $webhookId = $dsn->getRequiredOption('webhook_id'); 33 | $host = 'default' === $dsn->getHost() ? null : $dsn->getHost(); 34 | $port = $dsn->getPort(); 35 | 36 | return (new DiscordTransport($token, $webhookId, $this->client, $this->dispatcher))->setHost($host)->setPort($port); 37 | } 38 | 39 | protected function getSupportedSchemes(): array 40 | { 41 | return ['discord']; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Embeds/AbstractDiscordEmbed.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Symfony\Component\Notifier\Bridge\Discord\Embeds; 13 | 14 | /** 15 | * @author Karoly Gossler 16 | */ 17 | abstract class AbstractDiscordEmbed implements DiscordEmbedInterface 18 | { 19 | protected array $options = []; 20 | 21 | public function toArray(): array 22 | { 23 | return $this->options; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Embeds/AbstractDiscordEmbedObject.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Symfony\Component\Notifier\Bridge\Discord\Embeds; 13 | 14 | /** 15 | * @author Karoly Gossler 16 | */ 17 | abstract class AbstractDiscordEmbedObject implements DiscordEmbedObjectInterface 18 | { 19 | protected array $options = []; 20 | 21 | public function toArray(): array 22 | { 23 | return $this->options; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Embeds/DiscordAuthorEmbedObject.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Symfony\Component\Notifier\Bridge\Discord\Embeds; 13 | 14 | use Symfony\Component\Notifier\Exception\LengthException; 15 | 16 | /** 17 | * @author Karoly Gossler 18 | */ 19 | final class DiscordAuthorEmbedObject extends AbstractDiscordEmbedObject 20 | { 21 | private const NAME_LIMIT = 256; 22 | 23 | /** 24 | * @return $this 25 | */ 26 | public function name(string $name): static 27 | { 28 | if (mb_strlen($name, 'UTF-8') > self::NAME_LIMIT) { 29 | throw new LengthException(\sprintf('Maximum length for the name is %d characters.', self::NAME_LIMIT)); 30 | } 31 | 32 | $this->options['name'] = $name; 33 | 34 | return $this; 35 | } 36 | 37 | /** 38 | * @return $this 39 | */ 40 | public function url(string $url): static 41 | { 42 | $this->options['url'] = $url; 43 | 44 | return $this; 45 | } 46 | 47 | /** 48 | * @return $this 49 | */ 50 | public function iconUrl(string $iconUrl): static 51 | { 52 | $this->options['icon_url'] = $iconUrl; 53 | 54 | return $this; 55 | } 56 | 57 | /** 58 | * @return $this 59 | */ 60 | public function proxyIconUrl(string $proxyIconUrl): static 61 | { 62 | $this->options['proxy_icon_url'] = $proxyIconUrl; 63 | 64 | return $this; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /Embeds/DiscordEmbed.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Symfony\Component\Notifier\Bridge\Discord\Embeds; 13 | 14 | use Symfony\Component\Notifier\Exception\LengthException; 15 | 16 | /** 17 | * @author Karoly Gossler 18 | */ 19 | final class DiscordEmbed extends AbstractDiscordEmbed 20 | { 21 | private const TITLE_LIMIT = 256; 22 | private const DESCRIPTION_LIMIT = 4096; 23 | private const FIELDS_LIMIT = 25; 24 | 25 | /** 26 | * @return $this 27 | */ 28 | public function title(string $title): static 29 | { 30 | if (mb_strlen($title, 'UTF-8') > self::TITLE_LIMIT) { 31 | throw new LengthException(\sprintf('Maximum length for the title is %d characters.', self::TITLE_LIMIT)); 32 | } 33 | 34 | $this->options['title'] = $title; 35 | 36 | return $this; 37 | } 38 | 39 | /** 40 | * @return $this 41 | */ 42 | public function description(string $description): static 43 | { 44 | if (mb_strlen($description, 'UTF-8') > self::DESCRIPTION_LIMIT) { 45 | throw new LengthException(\sprintf('Maximum length for the description is %d characters.', self::DESCRIPTION_LIMIT)); 46 | } 47 | 48 | $this->options['description'] = $description; 49 | 50 | return $this; 51 | } 52 | 53 | /** 54 | * @return $this 55 | */ 56 | public function url(string $url): static 57 | { 58 | $this->options['url'] = $url; 59 | 60 | return $this; 61 | } 62 | 63 | /** 64 | * @return $this 65 | */ 66 | public function timestamp(\DateTimeInterface $timestamp): static 67 | { 68 | $this->options['timestamp'] = $timestamp->format(\DateTimeInterface::ISO8601); 69 | 70 | return $this; 71 | } 72 | 73 | /** 74 | * @return $this 75 | */ 76 | public function color(int $color): static 77 | { 78 | $this->options['color'] = $color; 79 | 80 | return $this; 81 | } 82 | 83 | /** 84 | * @return $this 85 | */ 86 | public function footer(DiscordFooterEmbedObject $footer): static 87 | { 88 | $this->options['footer'] = $footer->toArray(); 89 | 90 | return $this; 91 | } 92 | 93 | /** 94 | * @return $this 95 | */ 96 | public function thumbnail(DiscordMediaEmbedObject $thumbnail): static 97 | { 98 | $this->options['thumbnail'] = $thumbnail->toArray(); 99 | 100 | return $this; 101 | } 102 | 103 | /** 104 | * @return $this 105 | */ 106 | public function image(DiscordMediaEmbedObject $image): static 107 | { 108 | $this->options['image'] = $image->toArray(); 109 | 110 | return $this; 111 | } 112 | 113 | /** 114 | * @return $this 115 | */ 116 | public function author(DiscordAuthorEmbedObject $author): static 117 | { 118 | $this->options['author'] = $author->toArray(); 119 | 120 | return $this; 121 | } 122 | 123 | /** 124 | * @return $this 125 | */ 126 | public function addField(DiscordFieldEmbedObject $field): static 127 | { 128 | if (self::FIELDS_LIMIT === \count($this->options['fields'] ?? [])) { 129 | throw new LengthException(\sprintf('Maximum number of fields should not exceed %d.', self::FIELDS_LIMIT)); 130 | } 131 | 132 | if (!isset($this->options['fields'])) { 133 | $this->options['fields'] = []; 134 | } 135 | 136 | $this->options['fields'][] = $field->toArray(); 137 | 138 | return $this; 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /Embeds/DiscordEmbedInterface.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Symfony\Component\Notifier\Bridge\Discord\Embeds; 13 | 14 | /** 15 | * @author Karoly Gossler 16 | */ 17 | interface DiscordEmbedInterface 18 | { 19 | public function toArray(): array; 20 | } 21 | -------------------------------------------------------------------------------- /Embeds/DiscordEmbedObjectInterface.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Symfony\Component\Notifier\Bridge\Discord\Embeds; 13 | 14 | /** 15 | * @author Karoly Gossler 16 | */ 17 | interface DiscordEmbedObjectInterface 18 | { 19 | public function toArray(): array; 20 | } 21 | -------------------------------------------------------------------------------- /Embeds/DiscordFieldEmbedObject.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Symfony\Component\Notifier\Bridge\Discord\Embeds; 13 | 14 | use Symfony\Component\Notifier\Exception\LengthException; 15 | 16 | /** 17 | * @author Karoly Gossler 18 | */ 19 | final class DiscordFieldEmbedObject extends AbstractDiscordEmbedObject 20 | { 21 | private const NAME_LIMIT = 256; 22 | private const VALUE_LIMIT = 1024; 23 | 24 | /** 25 | * @return $this 26 | */ 27 | public function name(string $name): static 28 | { 29 | if (mb_strlen($name, 'UTF-8') > self::NAME_LIMIT) { 30 | throw new LengthException(\sprintf('Maximum length for the name is %d characters.', self::NAME_LIMIT)); 31 | } 32 | 33 | $this->options['name'] = $name; 34 | 35 | return $this; 36 | } 37 | 38 | /** 39 | * @return $this 40 | */ 41 | public function value(string $value): static 42 | { 43 | if (mb_strlen($value, 'UTF-8') > self::VALUE_LIMIT) { 44 | throw new LengthException(\sprintf('Maximum length for the value is %d characters.', self::VALUE_LIMIT)); 45 | } 46 | 47 | $this->options['value'] = $value; 48 | 49 | return $this; 50 | } 51 | 52 | /** 53 | * @return $this 54 | */ 55 | public function inline(bool $inline): static 56 | { 57 | $this->options['inline'] = $inline; 58 | 59 | return $this; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Embeds/DiscordFooterEmbedObject.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Symfony\Component\Notifier\Bridge\Discord\Embeds; 13 | 14 | use Symfony\Component\Notifier\Exception\LengthException; 15 | 16 | /** 17 | * @author Karoly Gossler 18 | */ 19 | final class DiscordFooterEmbedObject extends AbstractDiscordEmbedObject 20 | { 21 | private const TEXT_LIMIT = 2048; 22 | 23 | /** 24 | * @return $this 25 | */ 26 | public function text(string $text): static 27 | { 28 | if (mb_strlen($text, 'UTF-8') > self::TEXT_LIMIT) { 29 | throw new LengthException(\sprintf('Maximum length for the text is %d characters.', self::TEXT_LIMIT)); 30 | } 31 | 32 | $this->options['text'] = $text; 33 | 34 | return $this; 35 | } 36 | 37 | /** 38 | * @return $this 39 | */ 40 | public function iconUrl(string $iconUrl): static 41 | { 42 | $this->options['icon_url'] = $iconUrl; 43 | 44 | return $this; 45 | } 46 | 47 | /** 48 | * @return $this 49 | */ 50 | public function proxyIconUrl(string $proxyIconUrl): static 51 | { 52 | $this->options['proxy_icon_url'] = $proxyIconUrl; 53 | 54 | return $this; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Embeds/DiscordMediaEmbedObject.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Symfony\Component\Notifier\Bridge\Discord\Embeds; 13 | 14 | /** 15 | * @author Karoly Gossler 16 | */ 17 | class DiscordMediaEmbedObject extends AbstractDiscordEmbedObject 18 | { 19 | /** 20 | * @return $this 21 | */ 22 | public function url(string $url): static 23 | { 24 | $this->options['url'] = $url; 25 | 26 | return $this; 27 | } 28 | 29 | /** 30 | * @return $this 31 | */ 32 | public function proxyUrl(string $proxyUrl): static 33 | { 34 | $this->options['proxy_url'] = $proxyUrl; 35 | 36 | return $this; 37 | } 38 | 39 | /** 40 | * @return $this 41 | */ 42 | public function height(int $height): static 43 | { 44 | $this->options['height'] = $height; 45 | 46 | return $this; 47 | } 48 | 49 | /** 50 | * @return $this 51 | */ 52 | public function width(int $width): static 53 | { 54 | $this->options['width'] = $width; 55 | 56 | return $this; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2019-present Fabien Potencier 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is furnished 8 | to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Discord Notifier 2 | ================ 3 | 4 | Provides [Discord](https://discord.com) integration for Symfony Notifier. 5 | 6 | DSN example 7 | ----------- 8 | 9 | ``` 10 | DISCORD_DSN=discord://TOKEN@default?webhook_id=ID 11 | ``` 12 | 13 | where: 14 | - `TOKEN` the secure token of the webhook (returned for Incoming Webhooks) 15 | - `ID` the id of the webhook 16 | 17 | Adding Interactions to a Message 18 | -------------------------------- 19 | 20 | With a Discord message, you can use the `DiscordOptions` class to add some 21 | interactive options called Embed `elements`. 22 | 23 | ```php 24 | use Symfony\Component\Notifier\Bridge\Discord\DiscordOptions; 25 | use Symfony\Component\Notifier\Bridge\Discord\Embeds\DiscordEmbed; 26 | use Symfony\Component\Notifier\Bridge\Discord\Embeds\DiscordFieldEmbedObject; 27 | use Symfony\Component\Notifier\Bridge\Discord\Embeds\DiscordFooterEmbedObject; 28 | use Symfony\Component\Notifier\Bridge\Discord\Embeds\DiscordMediaEmbedObject; 29 | use Symfony\Component\Notifier\Message\ChatMessage; 30 | 31 | $chatMessage = new ChatMessage(''); 32 | 33 | // Create Discord Embed 34 | $discordOptions = (new DiscordOptions()) 35 | ->username('connor bot') 36 | ->addEmbed((new DiscordEmbed()) 37 | ->color(2021216) 38 | ->title('New song added!') 39 | ->thumbnail((new DiscordMediaEmbedObject()) 40 | ->url('https://i.scdn.co/image/ab67616d0000b2735eb27502aa5cb1b4c9db426b')) 41 | ->addField((new DiscordFieldEmbedObject()) 42 | ->name('Track') 43 | ->value('[Common Ground](https://open.spotify.com/track/36TYfGWUhIRlVjM8TxGUK6)') 44 | ->inline(true) 45 | ) 46 | ->addField((new DiscordFieldEmbedObject()) 47 | ->name('Artist') 48 | ->value('Alasdair Fraser') 49 | ->inline(true) 50 | ) 51 | ->addField((new DiscordFieldEmbedObject()) 52 | ->name('Album') 53 | ->value('Dawn Dance') 54 | ->inline(true) 55 | ) 56 | ->footer((new DiscordFooterEmbedObject()) 57 | ->text('Added ...') 58 | ->iconUrl('https://upload.wikimedia.org/wikipedia/commons/thumb/1/19/Spotify_logo_without_text.svg/200px-Spotify_logo_without_text.svg.png') 59 | ) 60 | ) 61 | ; 62 | 63 | // Add the custom options to the chat message and send the message 64 | $chatMessage->options($discordOptions); 65 | 66 | $chatter->send($chatMessage); 67 | ``` 68 | 69 | Resources 70 | --------- 71 | 72 | * [Contributing](https://symfony.com/doc/current/contributing/index.html) 73 | * [Report issues](https://github.com/symfony/symfony/issues) and 74 | [send Pull Requests](https://github.com/symfony/symfony/pulls) 75 | in the [main Symfony repository](https://github.com/symfony/symfony) 76 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "symfony/discord-notifier", 3 | "type": "symfony-notifier-bridge", 4 | "description": "Symfony Discord Notifier Bridge", 5 | "keywords": ["discord", "notifier"], 6 | "homepage": "https://symfony.com", 7 | "license": "MIT", 8 | "authors": [ 9 | { 10 | "name": "Fabien Potencier", 11 | "email": "fabien@symfony.com" 12 | }, 13 | { 14 | "name": "Symfony Community", 15 | "homepage": "https://symfony.com/contributors" 16 | } 17 | ], 18 | "require": { 19 | "php": ">=8.2", 20 | "symfony/http-client": "^6.4|^7.0", 21 | "symfony/notifier": "^7.2", 22 | "symfony/polyfill-mbstring": "^1.0" 23 | }, 24 | "autoload": { 25 | "psr-4": { "Symfony\\Component\\Notifier\\Bridge\\Discord\\": "" }, 26 | "exclude-from-classmap": [ 27 | "/Tests/" 28 | ] 29 | }, 30 | "minimum-stability": "dev" 31 | } 32 | --------------------------------------------------------------------------------