diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..cd8eb86e --- /dev/null +++ b/.editorconfig @@ -0,0 +1,15 @@ +; This file is for unifying the coding style for different editors and IDEs. +; More information at http://editorconfig.org + +root = true + +[*] +charset = utf-8 +indent_size = 4 +indent_style = space +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true + +[*.md] +trim_trailing_whitespace = false diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 00000000..0a2438ac --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,32 @@ +name: PHPUnit tests + +on: + - push + - pull_request + +jobs: + tests: + runs-on: ubuntu-latest + strategy: + fail-fast: true + matrix: + php: [8.2,8.1] + + name: Tests on PHP ${{ matrix.php }} - ${{ matrix.stability }} + + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + tools: composer:v2 + coverage: none + + - name: Install dependencies + run: composer update --prefer-source --no-interaction --no-progress + + - name: Execute tests + run: composer test tests diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..2942141e --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.idea/ +composer.lock +vendor/ diff --git a/.styleci.yml b/.styleci.yml new file mode 100644 index 00000000..0285f179 --- /dev/null +++ b/.styleci.yml @@ -0,0 +1 @@ +preset: laravel diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..6f296c23 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,7 @@ +# Changelog + +All notable changes to `ntfy` will be documented in this file + +## 1.0.0 - 201X-XX-XX + +- initial release diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..4da74e3f --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,55 @@ +# Contributing + +Contributions are **welcome** and will be fully **credited**. + +Please read and understand the contribution guide before creating an issue or pull request. + +## Etiquette + +This project is open source, and as such, the maintainers give their free time to build and maintain the source code +held within. They make the code freely available in the hope that it will be of use to other developers. It would be +extremely unfair for them to suffer abuse or anger for their hard work. + +Please be considerate towards maintainers when raising issues or presenting pull requests. Let's show the +world that developers are civilized and selfless people. + +It's the duty of the maintainer to ensure that all submissions to the project are of sufficient +quality to benefit the project. Many developers have different skillsets, strengths, and weaknesses. Respect the maintainer's decision, and do not be upset or abusive if your submission is not used. + +## Viability + +When requesting or submitting new features, first consider whether it might be useful to others. Open +source projects are used by many developers, who may have entirely different needs to your own. Think about +whether or not your feature is likely to be used by other users of the project. + +## Procedure + +Before filing an issue: + +- Attempt to replicate the problem, to ensure that it wasn't a coincidental incident. +- Check to make sure your feature suggestion isn't already present within the project. +- Check the pull requests tab to ensure that the bug doesn't have a fix in progress. +- Check the pull requests tab to ensure that the feature isn't already in progress. + +Before submitting a pull request: + +- Check the codebase to ensure that your feature doesn't already exist. +- Check the pull requests to ensure that another person hasn't already submitted the feature or fix. + +## Requirements + +If the project maintainer has any additional requirements, you will find them listed here. + +- **[PSR-2 Coding Standard](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md)** - The easiest way to apply the conventions is to install [PHP Code Sniffer](http://pear.php.net/package/PHP_CodeSniffer). + +- **Add tests!** - Your patch won't be accepted if it doesn't have tests. + +- **Document any change in behaviour** - Make sure the `README.md` and any other relevant documentation are kept up-to-date. + +- **Consider our release cycle** - We try to follow [SemVer v2.0.0](http://semver.org/). Randomly breaking public APIs is not an option. + +- **One pull request per feature** - If you want to do more than one thing, send multiple pull requests. + +- **Send coherent history** - Make sure each individual commit in your pull request is meaningful. If you had to make multiple intermediate commits while developing, please [squash them](http://www.git-scm.com/book/en/v2/Git-Tools-Rewriting-History#Changing-Multiple-Commit-Messages) before submitting. + +**Happy coding**! diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 00000000..9acbafd1 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,21 @@ +# The MIT License (MIT) + +Copyright (c) Cengiz Onkal + +> 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: +> +> The above copyright notice and this permission notice shall be included in +> all copies or substantial portions of the Software. +> +> 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. diff --git a/README.md b/README.md index fa8362b7..66cca30e 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,89 @@ -# New Notification Channels +Please see [this repo](https://github.com/laravel-notification-channels/channels) for instructions on how to submit a channel proposal. -### Suggesting a new channel -Have a suggestion or working on a new channel? Please create a new issue for that service. +# Ntfy -### I'm working on a new channel -Please create an issue for it if it does not already exist, then PR you code for review. +This package makes it easy to send notifications using [ntfy](https://ntfy.sh/) with Laravel 10 -## Workflow for new channels +## Contents -1) Head over to the [skeleton repo](https://github.com/laravel-notification-channels/skeleton) download a ZIP copy. This is important, to ensure you start from a fresh commit history. -2) Use find/replace to replace all of the placeholders with the correct values (package name, author name, email, etc). -3) Implement to logic for the channel & add tests. -4) Fork this repo, add it as a remote and push your new channel to a branch. -5) Submit a new PR against this repo for review. +- [Installation](#installation) + - [Setting up the ntfy service](#setting-up-the-ntfy-service) +- [Usage](#usage) + - [Available Message methods](#available-message-methods) +- [Testing](#testing) +- [Credits](#credits) +- [License](#license) -Take a look at our [FAQ](http://laravel-notification-channels.com/) to see our small list of rules, to provide top-notch notification channels. + +## Installation + +You can install the package via composer: + +```bash +composer require laravel-notification-channels/ntfy +``` +```php +// config/services.php +'providers' => [ + // ... + NotificationChannels\Ntfy\NtfyServiceProvider::class, +], +``` + +### Setting up the ntfy service + +Configure ntft service in `config/boardcasting.php`: + +```php + 'ntfy' => [ + 'driver' => 'ntfy', + 'host' => env('NTFY_HOST'), + 'port' => env('NTFY_PORT'), + 'username' => env('NTFY_USERNAME'), + 'password' => env('NTFY_PASSWORD'), + ], +``` +## Usage + +Now you can use the channel in your `via()` method inside the notification: + +```php + public function via(object $notifiable): array + { + return [NtfyChannel::class]; + } + + /** + * Get the mail representation of the notification. + */ + public function toNtfy(object $notifiable) + { + return (new NtfyMessage()) + ->topic('test') + ->title('Test Notification') + ->body('This is a test notification from Ntfy'); + } +``` + +### Available Message methods + +- `topic('')`: Accepts a string value for the topic of the notification. +- `title('')`: Accepts a string value for the title of the notification. +- `body('')`: Accepts a string value for the body of the notification. +- `priority('')`: Accepts a string value for the priority of the notification.(1=Min priority, 2=Low priority, 3=Default priority, 4=High priority, 5=Max priority) +- + + +## Testing + +``` bash +$ composer test +``` + +## Credits + +- [Cengiz Onkal](https://github.com/cengizonkal) + +## License + +The MIT License (MIT). Please see [License File](LICENSE.md) for more information. diff --git a/composer.json b/composer.json new file mode 100644 index 00000000..00a1dc20 --- /dev/null +++ b/composer.json @@ -0,0 +1,41 @@ +{ + "name": "laravel-notification-channels/ntfy", + "description": "Laravel notification channel for Ntfy", + "homepage": "https://github.com/laravel-notification-channels/ntfy", + "license": "MIT", + "authors": [ + { + "name": "Cengiz Onkal", + "email": "onkal.cengiz@gmail.com", + "homepage": "https://github.com/cengizonkal", + "role": "Developer" + } + ], + "require": { + "php": ">=8.1", + "guzzlehttp/guzzle": "^7.8", + "illuminate/notifications": "^10.0", + "illuminate/support": "^10.0" + }, + "require-dev": { + "mockery/mockery": "^1.0", + "phpunit/phpunit": "^9.0" + }, + "autoload": { + "psr-4": { + "NotificationChannels\\Ntfy\\": "src" + } + }, + "autoload-dev": { + "psr-4": { + "NotificationChannels\\Ntfy\\Test\\": "tests" + } + }, + "scripts": { + "test": "phpunit", + "test:coverage": "phpunit --coverage-text --coverage-clover=coverage.clover" + }, + "config": { + "sort-packages": true + } +} diff --git a/src/Exceptions/CouldNotSendNotification.php b/src/Exceptions/CouldNotSendNotification.php new file mode 100644 index 00000000..feaf94e2 --- /dev/null +++ b/src/Exceptions/CouldNotSendNotification.php @@ -0,0 +1,16 @@ +host = $host; + $this->username = $username; + $this->password = $password; + $this->client = new \GuzzleHttp\Client(); + $this->port = $port; + } + + public function send(NtfyMessage $message) + { + try { + $response = $this->client->post($this->host.':'.$this->port, [ + 'auth' => [$this->username, $this->password], + 'headers' => [ + 'Content-Type' => 'application/json', + 'Accept' => 'application/json', + ], + 'json' => [ + 'topic' => $message->topic, + 'message' => $message->content, + 'title' => $message->title, + 'priority' => $message->priority, + 'tags' => [], + 'attach' => '', + 'filename' => '', + 'click' => '', + 'actions' => [], + ], + ]); + } catch (\Exception $e) { + throw new CouldNotSendNotification($e); + } + + return $response; + } +} diff --git a/src/NtfyChannel.php b/src/NtfyChannel.php new file mode 100644 index 00000000..c45622f3 --- /dev/null +++ b/src/NtfyChannel.php @@ -0,0 +1,33 @@ +service = $service; + } + + /** + * Send the given notification. + * + * @param mixed $notifiable + * @param \Illuminate\Notifications\Notification $notification + * + * @throws \NotificationChannels\Ntfy\Exceptions\CouldNotSendNotification + */ + public function send($notifiable, Notification $notification) + { + //make sure that the $notification object has a toNtfy() method + if (! method_exists($notification, 'toNtfy')) { + throw CouldNotSendNotification::missingNtfyMethod(); + } + $this->service->send($notification->toNtfy($notifiable)); + } +} diff --git a/src/NtfyMessage.php b/src/NtfyMessage.php new file mode 100644 index 00000000..14949d0a --- /dev/null +++ b/src/NtfyMessage.php @@ -0,0 +1,55 @@ +title = $title; + + return $this; + } + + public function content(string $content): NtfyMessage + { + $this->content = $content; + + return $this; + } + + public function priority(int $priority): NtfyMessage + { + $this->priority = $priority; + + return $this; + } + + public function body(string $body): NtfyMessage + { + return $this->content($body); + } + + public function topic(string $topic): NtfyMessage + { + $this->topic = $topic; + + return $this; + } + + public function actions($actions): NtfyMessage + { + $this->actions = $actions; + + return $this; + } +} diff --git a/src/NtfyServiceProvider.php b/src/NtfyServiceProvider.php new file mode 100644 index 00000000..e94370fb --- /dev/null +++ b/src/NtfyServiceProvider.php @@ -0,0 +1,28 @@ +app->when(NtfyChannel::class) + ->needs(Ntfy::class) + ->give(function () { + $ntfyConfig = config('broadcasting.connections.ntfy'); + + return new Ntfy( + $ntfyConfig['host'], + $ntfyConfig['port'], + $ntfyConfig['username'], + $ntfyConfig['password'], + new \GuzzleHttp\Client() + ); + }); + } +} diff --git a/tests/NtfyChannelTest.php b/tests/NtfyChannelTest.php new file mode 100644 index 00000000..cc846f15 --- /dev/null +++ b/tests/NtfyChannelTest.php @@ -0,0 +1,19 @@ +expectException(\NotificationChannels\Ntfy\Exceptions\CouldNotSendNotification::class); + + $client = $this->getMockBuilder(\Psr\Http\Client\ClientInterface::class)->getMock(); + $channel = new NtfyChannel(new \NotificationChannels\Ntfy\Ntfy('host', 'port', 'username', 'password', $client)); + $notification = $this->getMockBuilder(\Illuminate\Notifications\Notification::class)->getMock(); + $channel->send(new \stdClass(), $notification); + } +} diff --git a/tests/NtfyMessageTest.php b/tests/NtfyMessageTest.php new file mode 100644 index 00000000..7d6544dc --- /dev/null +++ b/tests/NtfyMessageTest.php @@ -0,0 +1,49 @@ +body('body'); + $this->assertEquals('body', $message->content); + } + + public function testActions() + { + $message = new NtfyMessage(); + $message->actions(['action1', 'action2']); + $this->assertEquals(['action1', 'action2'], $message->actions); + } + + public function testContent() + { + $message = new NtfyMessage(); + $message->content('content'); + $this->assertEquals('content', $message->content); + } + + public function testTopic() + { + $message = new NtfyMessage(); + $message->topic('topic'); + $this->assertEquals('topic', $message->topic); + } + + public function testPriority() + { + $message = new NtfyMessage(); + $message->priority(1); + $this->assertEquals(1, $message->priority); + } + + public function testTitle() + { + $message = new NtfyMessage(); + $message->title('title'); + $this->assertEquals('title', $message->title); + } +}