├── CHANGELOG.md ├── LICENSE ├── README.md ├── RocketChatOptions.php ├── RocketChatTransport.php ├── RocketChatTransportFactory.php └── composer.json /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | CHANGELOG 2 | ========= 3 | 4 | 6.2 5 | --- 6 | 7 | * Add the ability to send multiple attachments in a message 8 | * Add the ability to send custom payload data to Rocket.Chat webhooks 9 | 10 | 5.3 11 | --- 12 | 13 | * The bridge is not marked as `@experimental` anymore 14 | 15 | 5.1.0 16 | ----- 17 | 18 | * Added the bridge 19 | -------------------------------------------------------------------------------- /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 | RocketChat Notifier 2 | =================== 3 | 4 | Provides [RocketChat](https://rocket.chat) integration for Symfony Notifier. 5 | 6 | DSN example 7 | ----------- 8 | 9 | ``` 10 | ROCKETCHAT_DSN=rocketchat://ACCESS_TOKEN@default?channel=CHANNEL 11 | ``` 12 | 13 | where: 14 | - `ACCESS_TOKEN` is your RocketChat webhook token 15 | - `CHANNEL` is your RocketChat channel, it may be overridden in the payload 16 | 17 | Example (be sure to escape the middle slash with %2F): 18 | 19 | ``` 20 | # Webhook URL: https://rocketchathost/hooks/a847c392165c41f7bc5bbf273dd701f3/9343289d1c33464bb15ef132b5a7628d 21 | ROCKETCHAT_DSN=rocketchat://a847c392165c41f7bc5bbf273dd701f3%2F9343289d1c33464bb15ef132b5a7628d@rocketchathost?channel=channel 22 | ``` 23 | 24 | Attachments and Payload 25 | ----------------------- 26 | 27 | When creating a `ChatMessage`, you can add payload and multiple attachments to 28 | `RocketChatOptions`. These enable you to customize the name or the avatar of the 29 | bot posting the message, and to add files to it. 30 | 31 | The payload can contain any data you want; its data is processed by a 32 | Rocket.Chat Incoming Webhook Script which you can write to best suit your needs. 33 | For example, you can use this script to send the raw payload to Rocket.Chat: 34 | 35 | ```javascript 36 | class Script { 37 | process_incoming_request({ request }) { 38 | return { 39 | request.content 40 | }; 41 | } 42 | } 43 | ``` 44 | 45 | When using this script, the Payload must be indexed following Rocket.Chat 46 | Payload convention: 47 | 48 | ```php 49 | $payload = [ 50 | 'alias' => 'Bot Name', 51 | 'emoji' => ':joy:', // Emoji used as avatar 52 | 'avatar' => 'http://site.com/logo.png', // Overridden by emoji if provided 53 | 'channel' => '#myChannel', // Overrides the DSN's channel setting 54 | ]; 55 | 56 | $attachement1 = [ 57 | 'color' => '#ff0000', 58 | 'title' => 'My title', 59 | 'text' => 'My text', 60 | // ... 61 | ]; 62 | 63 | $attachement2 = [ 64 | 'color' => '#ff0000', 65 | 'title' => 'My title', 66 | 'text' => 'My text', 67 | // ... 68 | ]; 69 | 70 | // For backward compatibility reasons, both usages are valid 71 | $rocketChatOptions = new RocketChatOptions($attachement1, $payload); 72 | $rocketChatOptions = new RocketChatOptions([$attachement1, $attachement2], $payload); 73 | ``` 74 | 75 | **Note:** the `text` and `attachments` keys of the payload will be overridden 76 | respectively by the ChatMessage's subject and the attachments provided in 77 | RocketChatOptions' constructor. 78 | 79 | See Also 80 | -------- 81 | 82 | * [Rocket.Chat Webhook Integration](https://docs.rocket.chat/guides/administration/admin-panel/integrations) 83 | * [Rocket.Chat Message Payload](https://developer.rocket.chat/reference/api/rest-api/endpoints/core-endpoints/chat-endpoints/postmessage#payload) 84 | 85 | Resources 86 | --------- 87 | 88 | * [Contributing](https://symfony.com/doc/current/contributing/index.html) 89 | * [Report issues](https://github.com/symfony/symfony/issues) and 90 | [send Pull Requests](https://github.com/symfony/symfony/pulls) 91 | in the [main Symfony repository](https://github.com/symfony/symfony) 92 | -------------------------------------------------------------------------------- /RocketChatOptions.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\RocketChat; 13 | 14 | use Symfony\Component\Notifier\Message\MessageOptionsInterface; 15 | 16 | /** 17 | * @author Jeroen Spee 18 | * 19 | * @see https://rocket.chat/docs/administrator-guides/integrations/ 20 | * @see https://developer.rocket.chat/reference/api/rest-api/endpoints/core-endpoints/chat-endpoints/postmessage 21 | */ 22 | final class RocketChatOptions implements MessageOptionsInterface 23 | { 24 | /** prefix with '@' for personal messages */ 25 | private ?string $channel = null; 26 | 27 | /** @var string[]|string[][] */ 28 | private array $attachments; 29 | 30 | /** @var string[] */ 31 | private array $payload; 32 | 33 | /** 34 | * @param string[]|string[][] $attachments 35 | * @param string[] $payload 36 | */ 37 | public function __construct(array $attachments = [], array $payload = []) 38 | { 39 | $this->attachments = $attachments; 40 | $this->payload = $payload; 41 | } 42 | 43 | public function toArray(): array 44 | { 45 | if ($this->attachments) { 46 | return $this->payload + ['attachments' => array_is_list($this->attachments) ? $this->attachments : [$this->attachments]]; 47 | } 48 | 49 | return $this->payload; 50 | } 51 | 52 | public function getRecipientId(): ?string 53 | { 54 | return $this->channel; 55 | } 56 | 57 | /** 58 | * @return $this 59 | */ 60 | public function channel(string $channel): static 61 | { 62 | $this->channel = $channel; 63 | 64 | return $this; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /RocketChatTransport.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\RocketChat; 13 | 14 | use Symfony\Component\Notifier\Exception\TransportException; 15 | use Symfony\Component\Notifier\Exception\UnsupportedMessageTypeException; 16 | use Symfony\Component\Notifier\Message\ChatMessage; 17 | use Symfony\Component\Notifier\Message\MessageInterface; 18 | use Symfony\Component\Notifier\Message\SentMessage; 19 | use Symfony\Component\Notifier\Transport\AbstractTransport; 20 | use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; 21 | use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; 22 | use Symfony\Contracts\HttpClient\HttpClientInterface; 23 | 24 | /** 25 | * @author Jeroen Spee 26 | */ 27 | final class RocketChatTransport extends AbstractTransport 28 | { 29 | protected const HOST = 'rocketchat.com'; 30 | 31 | public function __construct( 32 | #[\SensitiveParameter] private string $accessToken, 33 | private ?string $chatChannel = null, 34 | ?HttpClientInterface $client = null, 35 | ?EventDispatcherInterface $dispatcher = null, 36 | ) { 37 | parent::__construct($client, $dispatcher); 38 | } 39 | 40 | public function __toString(): string 41 | { 42 | return \sprintf('rocketchat://%s%s', $this->getEndpoint(), null !== $this->chatChannel ? '?channel='.$this->chatChannel : ''); 43 | } 44 | 45 | public function supports(MessageInterface $message): bool 46 | { 47 | return $message instanceof ChatMessage && (null === $message->getOptions() || $message->getOptions() instanceof RocketChatOptions); 48 | } 49 | 50 | /** 51 | * @see https://rocket.chat/docs/administrator-guides/integrations 52 | */ 53 | protected function doSend(MessageInterface $message): SentMessage 54 | { 55 | if (!$message instanceof ChatMessage) { 56 | throw new UnsupportedMessageTypeException(__CLASS__, ChatMessage::class, $message); 57 | } 58 | 59 | $options = $message->getOptions()?->toArray() ?? []; 60 | $options['channel'] ??= $message->getRecipientId() ?: $this->chatChannel; 61 | $options['text'] = $message->getSubject(); 62 | 63 | $endpoint = \sprintf('https://%s/hooks/%s', $this->getEndpoint(), $this->accessToken); 64 | $response = $this->client->request('POST', $endpoint, [ 65 | 'json' => array_filter($options), 66 | ]); 67 | 68 | try { 69 | $statusCode = $response->getStatusCode(); 70 | } catch (TransportExceptionInterface $e) { 71 | throw new TransportException('Could not reach the remote RocketChat server.', $response, 0, $e); 72 | } 73 | 74 | if (200 !== $statusCode) { 75 | throw new TransportException(\sprintf('Unable to post the RocketChat message: %s.', $response->getContent(false)), $response); 76 | } 77 | 78 | $result = $response->toArray(false); 79 | if (!$result['success']) { 80 | throw new TransportException(\sprintf('Unable to post the RocketChat message: %s.', $result['error']), $response); 81 | } 82 | 83 | $success = $response->toArray(false); 84 | 85 | $sentMessage = new SentMessage($message, (string) $this); 86 | 87 | if (isset($success['message']['_id'])) { 88 | $sentMessage->setMessageId($success['message']['_id']); 89 | } 90 | 91 | return $sentMessage; 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /RocketChatTransportFactory.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\RocketChat; 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 Jeroen Spee 20 | */ 21 | final class RocketChatTransportFactory extends AbstractTransportFactory 22 | { 23 | public function create(Dsn $dsn): RocketChatTransport 24 | { 25 | $scheme = $dsn->getScheme(); 26 | 27 | if ('rocketchat' !== $scheme) { 28 | throw new UnsupportedSchemeException($dsn, 'rocketchat', $this->getSupportedSchemes()); 29 | } 30 | 31 | $accessToken = $this->getUser($dsn); 32 | $channel = $dsn->getOption('channel'); 33 | $host = 'default' === $dsn->getHost() ? null : $dsn->getHost(); 34 | $port = $dsn->getPort(); 35 | 36 | return (new RocketChatTransport($accessToken, $channel, $this->client, $this->dispatcher))->setHost($host)->setPort($port); 37 | } 38 | 39 | protected function getSupportedSchemes(): array 40 | { 41 | return ['rocketchat']; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "symfony/rocket-chat-notifier", 3 | "type": "symfony-notifier-bridge", 4 | "description": "Symfony RocketChat Notifier Bridge", 5 | "keywords": ["rocketchat", "notifier"], 6 | "homepage": "https://symfony.com", 7 | "license": "MIT", 8 | "authors": [ 9 | { 10 | "name": "Jeroen Spee", 11 | "homepage": "https://github.com/Jeroeny" 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 | }, 23 | "autoload": { 24 | "psr-4": { "Symfony\\Component\\Notifier\\Bridge\\RocketChat\\": "" }, 25 | "exclude-from-classmap": [ 26 | "/Tests/" 27 | ] 28 | }, 29 | "minimum-stability": "dev" 30 | } 31 | --------------------------------------------------------------------------------