├── src ├── Facades │ └── Slack.php ├── Notifications │ └── SimpleSlack.php ├── ServiceProvider.php ├── Testing │ └── SlackFake.php └── Slack.php ├── LICENSE ├── composer.json ├── .github └── workflows │ └── php.yml ├── config └── laravel-slack.php └── README.md /src/Facades/Slack.php: -------------------------------------------------------------------------------- 1 | message = $message; 23 | } 24 | 25 | /** 26 | * Get the notification's delivery channels. 27 | * This channel will always be slack. 28 | * 29 | * @return array 30 | */ 31 | public function via() 32 | { 33 | return ['slack']; 34 | } 35 | 36 | /** 37 | * Get the Slack representation of the notification. 38 | * 39 | * @return SlackMessage 40 | */ 41 | public function toSlack() 42 | { 43 | return $this->message; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2018 Guilherme Pressutto 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /src/ServiceProvider.php: -------------------------------------------------------------------------------- 1 | mergeConfigFrom(self::CONFIG_PATH, 'laravel-slack'); 24 | 25 | $this->app->singleton(Slack::class, function ($app) { 26 | return new Slack($app['config']->get('laravel-slack')); 27 | }); 28 | 29 | $this->app->alias(Slack::class, 'slack'); 30 | } 31 | 32 | /** 33 | * Bootstrap the application events. 34 | * 35 | * @return void 36 | */ 37 | public function boot() 38 | { 39 | $this->publishes([self::CONFIG_PATH => config_path('laravel-slack.php')], 'config'); 40 | } 41 | 42 | /** 43 | * Get the services provided by the provider. 44 | * 45 | * @return array 46 | */ 47 | public function provides() 48 | { 49 | return [Slack::class]; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gpressutto5/laravel-slack", 3 | "description": "Slack notification for Laravel as it should be.", 4 | "keywords": [ 5 | "laravel", 6 | "slack" 7 | ], 8 | "type": "library", 9 | "license": "MIT", 10 | "authors": [ 11 | { 12 | "name": "Guilherme Pressutto", 13 | "email": "gpressutto5@gmail.com" 14 | } 15 | ], 16 | "require": { 17 | "php": ">=7.1.3", 18 | "guzzlehttp/guzzle": ">=6.3", 19 | "illuminate/notifications": ">=5.8", 20 | "illuminate/support": ">=5.8", 21 | "laravel/slack-notification-channel": ">=2.0" 22 | }, 23 | "require-dev": { 24 | "orchestra/testbench": "*", 25 | "phpunit/phpunit": ">=7.0" 26 | }, 27 | "autoload": { 28 | "psr-4": { 29 | "Pressutto\\LaravelSlack\\": "src" 30 | } 31 | }, 32 | "autoload-dev": { 33 | "psr-4": { 34 | "Tests\\": "tests" 35 | } 36 | }, 37 | "scripts": { 38 | "test": "vendor/bin/phpunit --colors=always" 39 | }, 40 | "minimum-stability": "stable", 41 | "extra": { 42 | "laravel": { 43 | "providers": [ 44 | "Pressutto\\LaravelSlack\\ServiceProvider" 45 | ], 46 | "aliases": { 47 | "Slack": "Pressutto\\LaravelSlack\\Facades\\Slack" 48 | } 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /.github/workflows/php.yml: -------------------------------------------------------------------------------- 1 | name: run-tests 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | pull_request: 7 | branches: [master] 8 | 9 | jobs: 10 | test: 11 | runs-on: ${{ matrix.os }} 12 | strategy: 13 | fail-fast: true 14 | matrix: 15 | os: [ubuntu-latest, windows-latest] 16 | php: [8.1] 17 | laravel: [9.*, 10.*] 18 | stability: [prefer-lowest, prefer-stable] 19 | include: 20 | - laravel: 9.* 21 | 22 | name: P${{ matrix.php }} - L${{ matrix.laravel }} - ${{ matrix.stability }} - ${{ matrix.os }} 23 | 24 | steps: 25 | - name: Checkout code 26 | uses: actions/checkout@v2 27 | 28 | - name: Setup PHP 29 | uses: shivammathur/setup-php@v2 30 | with: 31 | php-version: ${{ matrix.php }} 32 | extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, bcmath, soap, intl, gd, exif, iconv, imagick, fileinfo 33 | coverage: none 34 | 35 | - name: Setup problem matchers 36 | run: | 37 | echo "::add-matcher::${{ runner.tool_cache }}/php.json" 38 | echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json" 39 | - name: Install dependencies 40 | run: | 41 | composer require "laravel/framework:${{ matrix.laravel }}" --no-interaction --no-update 42 | composer update --${{ matrix.stability }} --prefer-dist --no-interaction 43 | - name: Execute tests 44 | run: vendor/bin/phpunit 45 | -------------------------------------------------------------------------------- /config/laravel-slack.php: -------------------------------------------------------------------------------- 1 | env('SLACK_WEBHOOK_URL', ''), 17 | 18 | /* 19 | |-------------------------------------------------------------------------- 20 | | Default Channel 21 | |-------------------------------------------------------------------------- 22 | | 23 | | If no recipient is specified the message will delivered to this channel. 24 | | You can set a default user by using '@' instead of '#' 25 | | 26 | */ 27 | 28 | 'default_channel' => '#general', 29 | 30 | /* 31 | |-------------------------------------------------------------------------- 32 | | Application Name 33 | |-------------------------------------------------------------------------- 34 | | 35 | | The username that this integration will post as. 36 | | Leave null to use Slack's default. 37 | | 38 | */ 39 | 40 | 'application_name' => env('APP_NAME', null), 41 | 42 | /* 43 | |-------------------------------------------------------------------------- 44 | | Application Image 45 | |-------------------------------------------------------------------------- 46 | | 47 | | The user image that is used for messages from this integration. 48 | | Leave null to use Slack's default. 49 | | It should be a valid URL. 50 | | 51 | */ 52 | 53 | 'application_image' => null, 54 | 55 | ]; 56 | -------------------------------------------------------------------------------- /src/Testing/SlackFake.php: -------------------------------------------------------------------------------- 1 | sent; 26 | } 27 | 28 | /** 29 | * Assert that at least, or exactly(if $strict is true) 30 | * $count messages passed to $callable 31 | * returns true. 32 | * 33 | * @param callable $callback 34 | * @param int $count 35 | * @param bool $strict 36 | */ 37 | public function assertSent(callable $callback, int $count = 1, bool $strict = false) 38 | { 39 | $sentCount = 0; 40 | 41 | foreach ($this->sentMessages() as $sentMessage) { 42 | if ($callback($sentMessage)) { 43 | $sentCount++; 44 | } 45 | } 46 | 47 | $passed = $strict ? $sentCount === $count : $sentCount >= $count; 48 | 49 | PHPUnit::assertTrue($passed, 50 | "The number of messages sent was {$sentCount} instead of {$count}"); 51 | } 52 | 53 | /** 54 | * Asserts $count exactly messages were sent. 55 | * 56 | * @param int $count 57 | */ 58 | public function assertSentCount(int $count) 59 | { 60 | $this->assertSent(function () { 61 | return true; 62 | }, $count, true); 63 | } 64 | 65 | protected function notify(SlackMessage $slackMessage) 66 | { 67 | $this->sent[] = $slackMessage; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/Slack.php: -------------------------------------------------------------------------------- 1 | anonymousNotifiable = Notification::route('slack', $config['slack_webhook_url']); 41 | $this->recipients = [$config['default_channel']]; 42 | $this->from = $config['application_name']; 43 | $this->image = $config['application_image']; 44 | $this->config = $config; 45 | } 46 | 47 | /** 48 | * Allows user specify webhook to use 49 | * for current instance. 50 | * 51 | * @param string $url 52 | * @return $this 53 | */ 54 | public function webhook(string $url): self 55 | { 56 | $this->anonymousNotifiable = Notification::route('slack', $url); 57 | 58 | return $this; 59 | } 60 | 61 | /** 62 | * Set the recipients of the message. 63 | * 64 | * @param object|array|string $recipient 65 | * @return $this 66 | */ 67 | public function to($recipient): self 68 | { 69 | if ($recipient instanceof Collection) { 70 | $recipient = $recipient->all(); 71 | } 72 | 73 | $recipients = is_array($recipient) ? $recipient : func_get_args(); 74 | 75 | $this->recipients = array_map( 76 | function ($recipient) { 77 | if (is_object($recipient)) { 78 | return $recipient->slack_channel; 79 | } 80 | 81 | return $recipient; 82 | }, $recipients 83 | ); 84 | 85 | return $this; 86 | } 87 | 88 | /** 89 | * Send a new message. 90 | * 91 | * @param string|SlackMessage $message 92 | * @return void 93 | */ 94 | public function send($message) 95 | { 96 | $slackMessages = $this->getSlackMessageArray($message); 97 | 98 | foreach ($slackMessages as $slackMessage) { 99 | $this->notify($slackMessage); 100 | } 101 | 102 | $this->recipients = [$this->config['default_channel']]; 103 | } 104 | 105 | protected function notify(SlackMessage $slackMessage) 106 | { 107 | $this->anonymousNotifiable->notify(new SimpleSlack($slackMessage)); 108 | } 109 | 110 | /** 111 | * Send a new message. 112 | * 113 | * @param string|SlackMessage $message 114 | * @return SlackMessage[] 115 | */ 116 | protected function getSlackMessageArray($message): array 117 | { 118 | if ($message instanceof SlackMessage) { 119 | return [$message]; 120 | } 121 | 122 | $slackMessageArray = []; 123 | $slackMessage = (new SlackMessage())->content($message); 124 | 125 | if ($this->from) { 126 | $slackMessage->from($this->from); 127 | } 128 | 129 | if ($this->image) { 130 | $slackMessage->image($this->image); 131 | } 132 | 133 | foreach ($this->recipients as $recipient) { 134 | $messageClone = clone $slackMessage; 135 | $slackMessageArray[] = $messageClone->to($recipient); 136 | } 137 | 138 | return $slackMessageArray; 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 | Build Status 5 | codecov 6 | Latest Stable Version 7 | PHP from Packagist 8 | Laravel Version 9 | Total Downloads 10 | License 11 |
12 | Based on illuminate/mail 13 |

14 | 15 | ## About Laravel Slack 16 | 17 | Slack notification for Laravel as it should be. 18 | Easy, fast, simple and **highly testable**. 19 | Since it uses On-Demand Notifications, it requires Laravel 5.5 or higher. 20 | 21 | > **This library is archived** and no longer maintained. It works as expected, but I don't have time to maintain it anymore. As a last update, I've removed version constraints from the `composer.json` file, so you can use it with any future Laravel versions. Feel free to fork it and use it as you wish. 22 | 23 | ## Installation 24 | 25 | Require this package in your composer.json and update your dependencies: 26 | 27 | ```bash 28 | composer require gpressutto5/laravel-slack 29 | ``` 30 | 31 | Since this package supports Laravel's Package Auto-Discovery 32 | you don't need to manually register the ServiceProvider. 33 | 34 | After that, publish the configuration file: 35 | 36 | ```bash 37 | php artisan vendor:publish --provider="Pressutto\LaravelSlack\ServiceProvider" 38 | ``` 39 | 40 | You're gonna need to configure an ["Incoming Webhook"](https://api.slack.com/incoming-webhooks) integration for your Slack team. 41 | 42 | ## Configuration 43 | 44 | On the published configuration file `config/laravel-slack.php` 45 | you can change options like the Webhook URL, the default channel, 46 | the application name and the application image. 47 | 48 | For security reasons you shouldn't commit your Webhook URL, 49 | so this package will, by default, use the environment variable 50 | `SLACK_WEBHOOK_URL`. You can just add it to your `.env` file. 51 | Like this: 52 | 53 | ```dotenv 54 | SLACK_WEBHOOK_URL=https://hooks.slack.com/services/XXXXXXXXX/XXXXXXXXX/XXXXXXXXXXXXXXXXXXXXXXXX 55 | ``` 56 | 57 | ## Usage 58 | 59 | You can send simple Slack messages like this: 60 | 61 | - Send message to a channel: 62 | 63 | ```php 64 | \Slack::to('#finance')->send('Hey, finance channel! A new order was created just now!'); 65 | ``` 66 | 67 | - Send message to an user: 68 | 69 | ```php 70 | \Slack::to('@joe')->send("Hey Joe! It looks like you've forgotten your password! Use this token to recover it: as34bhdfh"); 71 | ``` 72 | 73 | - Send message to multiple users: 74 | 75 | ```php 76 | \Slack::to(['@zoe', '@amy', '@mia'])->send('I swear, honey, you are the only one... :heart:'); 77 | // ↑ look at this array ↑ 78 | ``` 79 | 80 | - Mix it up: 81 | 82 | ```php 83 | \Slack::to('#universe', '@god', '#scientists')->send(':thinking_face:'); 84 | // ↑ what? I don't need that array? ↑ 85 | ``` 86 | 87 | - No recipient: 88 | 89 | ```php 90 | \Slack::send('Default message to the default channel, set on config/laravel-slack.php.'); 91 | ``` 92 | 93 | - Send SlackMessage objects: 94 | 95 | ```php 96 | class HelloMessage extends SlackMessage 97 | { 98 | public $content = "Hey bob, I'm a sending a custom SlackMessage"; 99 | public $channel = '@bob'; 100 | } 101 | \Slack::send(new SlackMessage()); 102 | ``` 103 | 104 | - Send to user: 105 | 106 | You can use any object as a recipient as long as they have the 107 | property `slack_channel`. If you are using Models you can just 108 | create the column `slack_channel` and store the `@username` or 109 | the `#channel` name there. If you already store it but on a 110 | different column you can create a method `getSlackChannelAttribute`. 111 | 112 | ```php 113 | class User extends Model 114 | { 115 | public function getSlackChannelAttribute(): string 116 | { 117 | return $this->attributes['my_custom_slack_channel_column']; 118 | } 119 | } 120 | \Slack::to(User::where('verified', true))->send('Sending message to all verified users!'); 121 | ``` 122 | 123 | - Send message by specifying webhook: 124 | 125 | ```php 126 | \Slack::to('#finance')->webhook('https://hooks.slack.com/services/XXXXXXXXX/XXXXXXXXX/XXXXXXXXXXXXXXXXXXXXXXXX')->send('Hey, finance channel! A new order was created just now!'); 127 | ``` 128 | 129 | ## Testing 130 | 131 | When testing you can easily mock the Slack service by calling 132 | `Slack::fake()` it will return a `SlackFake` object that won't 133 | send any message for real and will save them to an array. 134 | You can get this array by calling `Slack::sentMessages()`. 135 | 136 | This class also has some helper methods for you to use when 137 | testing: 138 | 139 | - Assert that at least one message with the content 'fake' was sent: 140 | 141 | ```php 142 | Slack::assertSent(function (SlackMessage $message) { 143 | return $message->content === 'fake'; 144 | }); 145 | ``` 146 | 147 | - Assert that at least two messages with the content 148 | being a string longer than 5 characters were sent: 149 | 150 | ```php 151 | Slack::assertSent(function (SlackMessage $message) { 152 | return strlen($message->content) >= 100; 153 | }, 2); 154 | ``` 155 | 156 | - Assert that exactly five messages where the content 157 | content contains the word 'test' were sent: 158 | 159 | ```php 160 | Slack::assertSent(function (SlackMessage $message) { 161 | return strpos($message->content, 'test') !== false; 162 | }, 5, true); 163 | ``` 164 | 165 | - Assert that exactly three messages were sent: 166 | 167 | ```php 168 | Slack::assertSentCount(3); 169 | ``` 170 | 171 | Since this package uses `illuminate/notifications` to send notifications 172 | you can mock the Notification service instead of the Slack one 173 | and use the class `NotificationFake` in your tests. 174 | [Take a look](https://laravel.com/docs/8.x/mocking#notification-fake). 175 | --------------------------------------------------------------------------------