diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a53bf5e --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +/vendor +composer.lock +/phpunit.xml +.phpunit.result.cache +.idea \ No newline at end of file diff --git a/.phpunit.cache/test-results b/.phpunit.cache/test-results new file mode 100644 index 0000000..666edf5 --- /dev/null +++ b/.phpunit.cache/test-results @@ -0,0 +1 @@ +{"version":1,"defects":{"Xefi\\SmsFactor\\Tests\\Unit\\Channels\\SmsFactorSmsChannelTest::testSmsIsSentViaSmsFactor":8},"times":{"Xefi\\SmsFactor\\Tests\\Unit\\Channels\\SmsFactorSmsChannelTest::testSmsIsSentViaSmsFactor":0.04,"Xefi\\SmsFactor\\Tests\\Unit\\Channels\\SmsFactorSmsChannelTest::testSmsIsSentViaSmsFactorWithCustomSender":0,"Xefi\\SmsFactor\\Tests\\Unit\\Channels\\SmsFactorSmsChannelTest::testSmsIsSentViaSmsFactorWithCustomDelay":0,"Xefi\\SmsFactor\\Tests\\Unit\\Channels\\SmsFactorSmsChannelTest::testSmsIsSentViaSmsFactorWithCustomPushType":0,"Xefi\\SmsFactor\\Tests\\Unit\\Channels\\SmsFactorSmsChannelTest::testSmsIsSentViaSmsFactorWithCustomGsmsmsid":0}} \ No newline at end of file diff --git a/.styleci.yml b/.styleci.yml new file mode 100644 index 0000000..e04bb81 --- /dev/null +++ b/.styleci.yml @@ -0,0 +1,4 @@ +php: + preset: laravel +js: true +css: true \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..10397c8 --- /dev/null +++ b/README.md @@ -0,0 +1,66 @@ +# Laravel SMSFactor Notification Channel + +## Prerequisites + +Before you can send notifications via SMSFactor, you need to install the `xefi/sms-factor-notification-channel` + +```bash +composer require xefi/sms-factor-notification-channel +``` + +The package includes a [configuration file](https://github.com/xefi/sms-factor-notification-channel/config/sms-factor.php). However, you are not required to export this configuration file to your own application. You can simply use the `SMS_FACTOR_TOKEN` environment variables to define your SMSFactor token. + +``` +SMS_FACTOR_TOKEN=your-token +``` + +## Formating SMS Notifications + +If a notification supports being sent as an SMS, you should define a `toSmsFactor` method on the notification class. This method will receive a $notifiable entity and should return an `Xefi\SmsFactor\Messages\SmsFactorMessage` instance: + +```php +use Xefi\SmsFactor\Messages\SmsFactorMessage; + +/** + * Get the SMSFactor representation of the notification. + */ +public function toSmsFactor(object $notifiable): SmsFactorMessage +{ + return (new SmsFactorMessage) + ->text('Your SMS message content'); +} +``` + +## Customizing the "To" Number + +If you would like to customize the number depending on the notifiable object you are calling, you'll need to implement the `routeNotificationForSmsFactor` method on your notifiable model: + +```php +use Illuminate\Notifications\Notification; + +/** + * Get the corresponding phone number for the current model. + */ +public function routeNotificationForSmsFactor(Notification $notification) +{ + return $this->phone; +} +``` + +## Customizing the "From" Number + +If you would like to send some notifications from a phone number that is different from the phone number specified by your `SMS_FACTOR_SMS_FROM` environment variable, you may call the `sender` method on a `SmsFactorMessage` instance: + +```php +use Xefi\SmsFactor\Messages\SmsFactorMessage; + +/** + * Get the SMSFactor representation of the notification. + */ +public function toSmsFactor(object $notifiable): SmsFactorMessage +{ + return (new SmsFactorMessage) + ->content('Your SMS message content') + ->sender('15554443333'); +} +``` diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..c528aa3 --- /dev/null +++ b/composer.json @@ -0,0 +1,53 @@ +{ + "name": "xefi/sms-factor-notification-channel", + "description": "Sms Factor Notification Channel for laravel.", + "keywords": ["laravel", "notifications", "smsfactor"], + "license": "MIT", + "authors": [ + { + "name": "Gautier Deleglise", + "email": "g.deleglise@xefi.fr" + } + ], + "require": { + "php": "^8.0", + "illuminate/notifications": "^8.0|^9.0|^10.0|^11.0", + "illuminate/support": "^8.0|^9.0|^10.0|^11.0", + "smsfactor/smsfactor-php-sdk": "^1.0.5" + }, + "require-dev": { + "guzzlehttp/guzzle": "^7.2", + "mockery/mockery": "^1.0", + "orchestra/testbench": "^6.0|^7.0|^8.0|^9.0", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^9.0|^10.4" + }, + "autoload": { + "psr-4": { + "Xefi\\SmsFactor\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "Xefi\\SmsFactor\\Tests\\": "tests/" + } + }, + "config": { + "sort-packages": true, + "allow-plugins": { + "composer/package-versions-deprecated": true + } + }, + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + }, + "laravel": { + "providers": [ + "Xefi\\SmsFactor\\SmsFactorChannelServiceProvider" + ] + } + }, + "minimum-stability": "dev", + "prefer-stable": true +} \ No newline at end of file diff --git a/config/sms-factor.php b/config/sms-factor.php new file mode 100644 index 0000000..2bc14b9 --- /dev/null +++ b/config/sms-factor.php @@ -0,0 +1,28 @@ + env('SMS_FACTOR_SMS_FROM'), + + /* + |-------------------------------------------------------------------------- + | API Token + |-------------------------------------------------------------------------- + | + | This configuration contain your API token, which may be accessed + | from your SMSFactor dashboard. + | + */ + + 'api_token' => env('SMS_FACTOR_TOKEN'), +]; \ No newline at end of file diff --git a/phpunit.xml.dist b/phpunit.xml.dist new file mode 100644 index 0000000..a6da89d --- /dev/null +++ b/phpunit.xml.dist @@ -0,0 +1,8 @@ + + + + + ./tests/Unit + + + diff --git a/src/Channels/SmsFactorSmsChannel.php b/src/Channels/SmsFactorSmsChannel.php new file mode 100644 index 0000000..f8496f8 --- /dev/null +++ b/src/Channels/SmsFactorSmsChannel.php @@ -0,0 +1,39 @@ +routeNotificationFor('sms-factor', $notification)) { + return; + } + + $message = $notification->toSmsFactor($notifiable); + + if (is_string($message)) { + $message = (new SmsFactorMessage())->text($message); + } + + return Message::send([ + 'to' => $to, + 'text' => trim($message->text), + 'delay' => $message->delay, + 'pushtype' => $message->pushtype ?? null, + 'sender' => $message->sender ?? config('sms-factor.sms_from'), + 'gsmsmsid' => $message->gsmsmsid ?? null, + ]); + } +} \ No newline at end of file diff --git a/src/Messages/SmsFactorMessage.php b/src/Messages/SmsFactorMessage.php new file mode 100644 index 0000000..77242fb --- /dev/null +++ b/src/Messages/SmsFactorMessage.php @@ -0,0 +1,112 @@ +text = $text; + + return $this; + } + + /** + * Set the message pushtype. + * + * @param string $pushtype + * @return $this + */ + public function pushtype($pushtype) + { + $this->pushtype = $pushtype; + + return $this; + } + + /** + * Set the message delay. + * + * @param \Carbon\Carbon $delay + * @return $this + */ + public function delay($delay) + { + if ($delay instanceof Carbon) { + $delay = $delay->format('Y-m-d H:i:s'); + } + + $this->delay = $delay; + + return $this; + } + + /** + * Set the message sender. + * + * @param string $sender + * @return $this + */ + public function sender($sender) + { + $this->sender = $sender; + + return $this; + } + + /** + * Set the message gsmsmsid. + * + * @param string $gsmsmsid + * @return $this + */ + public function gsmsmsid($gsmsmsid) + { + $this->gsmsmsid = $gsmsmsid; + + return $this; + } +} \ No newline at end of file diff --git a/src/SmsFactorChannelServiceProvider.php b/src/SmsFactorChannelServiceProvider.php new file mode 100644 index 0000000..3573115 --- /dev/null +++ b/src/SmsFactorChannelServiceProvider.php @@ -0,0 +1,43 @@ +mergeConfigFrom(__DIR__ . '/../config/sms-factor.php', 'sms-factor'); + + Notification::resolved(function (ChannelManager $service) { + $service->extend('sms-factor', function ($app) { + return $app->make(SmsFactorSmsChannel::class); + }); + }); + } + + /** + * Bootstrap the application services. + * + * @return void + */ + public function boot() + { + \SMSFactor\SMSFactor::setApiToken(config('sms-factor.api_token')); + + if ($this->app->runningInConsole()) { + $this->publishes([ + __DIR__.'/../config/sms-factor.php' => $this->app->configPath('sms-factor.php'), + ], 'sms-factor'); + } + } +} \ No newline at end of file diff --git a/tests/Unit/Channels/SmsFactorSmsChannelTest.php b/tests/Unit/Channels/SmsFactorSmsChannelTest.php new file mode 100644 index 0000000..92efb53 --- /dev/null +++ b/tests/Unit/Channels/SmsFactorSmsChannelTest.php @@ -0,0 +1,201 @@ +mock('alias:'.Message::class, function (MockInterface $mock) { + $mock->shouldReceive('send') + ->with( + [ + 'to' => '5555555555', + 'text' => 'this is my message', + 'delay' => null, + 'pushtype' => null, + 'sender' => null, + 'gsmsmsid' => null, + ] + ) + ->once(); + }); + + $channel->send($notifiable, $notification); + } + + public function testSmsIsSentViaSmsFactorWithCustomSender() + { + $notification = new NotificationSmsFactorSmsChannelTestCustomSenderNotification; + $notifiable = new NotificationSmsFactorSmsChannelTestNotifiable; + + $channel = new SmsFactorSmsChannel(); + + $this->mock('alias:'.Message::class, function (MockInterface $mock) { + $mock->shouldReceive('send') + ->with( + [ + 'to' => '5555555555', + 'text' => 'this is my message', + 'delay' => null, + 'pushtype' => null, + 'sender' => '12345', + 'gsmsmsid' => null, + ] + ) + ->once(); + }); + + $channel->send($notifiable, $notification); + } + + public function testSmsIsSentViaSmsFactorWithCustomDelay() + { + $notification = new NotificationSmsFactorSmsChannelTestCustomDelayNotification; + $notifiable = new NotificationSmsFactorSmsChannelTestNotifiable; + + $channel = new SmsFactorSmsChannel(); + + $this->mock('alias:'.Message::class, function (MockInterface $mock) { + $mock->shouldReceive('send') + ->with( + [ + 'to' => '5555555555', + 'text' => 'this is my message', + 'delay' => Carbon::create(2024, 01, 01)->format('Y-m-d H:i:s'), + 'pushtype' => null, + 'sender' => null, + 'gsmsmsid' => null, + ] + ) + ->once(); + }); + + $channel->send($notifiable, $notification); + } + + public function testSmsIsSentViaSmsFactorWithCustomPushType() + { + $notification = new NotificationSmsFactorSmsChannelTestCustomPushTypeNotification; + $notifiable = new NotificationSmsFactorSmsChannelTestNotifiable; + + $channel = new SmsFactorSmsChannel(); + + $this->mock('alias:'.Message::class, function (MockInterface $mock) { + $mock->shouldReceive('send') + ->with( + [ + 'to' => '5555555555', + 'text' => 'this is my message', + 'delay' => null, + 'pushtype' => 'alert', + 'sender' => null, + 'gsmsmsid' => null, + ] + ) + ->once(); + }); + + $channel->send($notifiable, $notification); + } + + public function testSmsIsSentViaSmsFactorWithCustomGsmsmsid() + { + $notification = new NotificationSmsFactorSmsChannelTestCustomGsmsmsidNotification; + $notifiable = new NotificationSmsFactorSmsChannelTestNotifiable; + + $channel = new SmsFactorSmsChannel(); + + $this->mock('alias:'.Message::class, function (MockInterface $mock) { + $mock->shouldReceive('send') + ->with( + [ + 'to' => '5555555555', + 'text' => 'this is my message', + 'delay' => null, + 'pushtype' => null, + 'sender' => null, + 'gsmsmsid' => 'my-custom-id', + ] + ) + ->once(); + }); + + $channel->send($notifiable, $notification); + } +} + +class NotificationSmsFactorSmsChannelTestNotification extends Notification +{ + public function toSmsFactor($notifiable) + { + return (new SmsFactorMessage())->text('this is my message'); + } +} + +class NotificationSmsFactorSmsChannelTestCustomSenderNotification extends Notification +{ + public function toSmsFactor($notifiable) + { + return (new SmsFactorMessage())->text('this is my message')->sender('12345'); + } +} + +class NotificationSmsFactorSmsChannelTestCustomDelayNotification extends Notification +{ + public function toSmsFactor($notifiable) + { + return (new SmsFactorMessage())->text('this is my message')->delay(Carbon::create(2024, 01, 01)); + } +} + +class NotificationSmsFactorSmsChannelTestCustomPushTypeNotification extends Notification +{ + public function toSmsFactor($notifiable) + { + return (new SmsFactorMessage())->text('this is my message')->pushtype('alert'); + } +} + +class NotificationSmsFactorSmsChannelTestCustomGsmsmsidNotification extends Notification +{ + public function toSmsFactor($notifiable) + { + return (new SmsFactorMessage())->text('this is my message')->gsmsmsid('my-custom-id'); + } +} + +class NotificationSmsFactorSmsChannelTestNotifiable +{ + use Notifiable; + + public $phone_number = '5555555555'; + + public function routeNotificationForSmsFactor($notification) + { + return $this->phone_number; + } +} \ No newline at end of file