diff --git a/.env.example b/.env.example index e2e0b18720..7e3a0e6e87 100644 --- a/.env.example +++ b/.env.example @@ -178,6 +178,17 @@ MANDRILL_SECRET= SPARKPOST_SECRET= MAILERSEND_API_KEY= +# +# Ntfy notification settings. +# defaults to "https://ntfy.sh", but needs a topic or it won't work. +# authentication is recommended but not required. +# +NTFY_SERVER= +NTFY_TOPIC= +NTFY_AUTH_ENABLED=false +NTFY_AUTH_USERNAME= +NTFY_AUTH_PASSWORD= + # Firefly III can send you the following messages. SEND_ERROR_MESSAGE=true diff --git a/app/Handlers/Events/AdminEventHandler.php b/app/Handlers/Events/AdminEventHandler.php index 9c450a537d..f6b5468355 100644 --- a/app/Handlers/Events/AdminEventHandler.php +++ b/app/Handlers/Events/AdminEventHandler.php @@ -27,11 +27,11 @@ use FireflyIII\Events\Admin\InvitationCreated; use FireflyIII\Events\AdminRequestedTestMessage; use FireflyIII\Events\NewVersionAvailable; use FireflyIII\Events\Test\TestNotificationChannel; -use FireflyIII\Notifications\Admin\TestNotification; use FireflyIII\Notifications\Admin\UserInvitation; use FireflyIII\Notifications\Admin\VersionCheckResult; use FireflyIII\Notifications\Test\TestNotificationDiscord; use FireflyIII\Notifications\Test\TestNotificationEmail; +use FireflyIII\Notifications\Test\TestNotificationNtfy; use FireflyIII\Notifications\Test\TestNotificationSlack; use FireflyIII\Repositories\User\UserRepositoryInterface; use Illuminate\Support\Facades\Log; @@ -131,8 +131,8 @@ class AdminEventHandler case 'slack': $class = TestNotificationSlack::class; break; - case 'discord': - $class = TestNotificationDiscord::class; + case 'ntfy': + $class = TestNotificationNtfy::class; break; default: app('log')->error(sprintf('Unknown channel "%s" in sendTestNotification method.', $event->channel)); diff --git a/app/Http/Controllers/Admin/NotificationController.php b/app/Http/Controllers/Admin/NotificationController.php index 59b32d7fef..a8fcc84e7e 100644 --- a/app/Http/Controllers/Admin/NotificationController.php +++ b/app/Http/Controllers/Admin/NotificationController.php @@ -42,6 +42,8 @@ class NotificationController extends Controller $subTitleIcon = 'envelope-o'; $slackUrl = app('fireflyconfig')->get('slack_webhook_url', '')->data; $channels = config('notifications.channels'); + $forcedAvailability = []; + // admin notification settings: @@ -52,8 +54,18 @@ class NotificationController extends Controller } } + // loop all channels + foreach ($channels as $channel => $info) { + $forcedAvailability[$channel] = true; + } - return view('admin.notifications.index', compact('title', 'subTitle', 'mainTitleIcon', 'subTitleIcon', 'channels', 'slackUrl', 'notifications')); + // validate presence of of Ntfy settings. + if('' === (string)config('ntfy-notification-channel.topic')) { + Log::warning('No topic name for Ntfy, channel is disabled.'); + $forcedAvailability['ntfy'] = false; + } + + return view('admin.notifications.index', compact('title', 'subTitle', 'forcedAvailability', 'mainTitleIcon', 'subTitleIcon', 'channels', 'slackUrl', 'notifications')); } public function postIndex(NotificationRequest $request): RedirectResponse @@ -89,6 +101,7 @@ class NotificationController extends Controller break; case 'email': case 'slack': + case 'ntfy': /** @var User $user */ $user = auth()->user(); app('log')->debug(sprintf('Now in testNotification("%s") controller.', $channel)); diff --git a/app/Notifications/Admin/TestNotification.php b/app/Notifications/Admin/TestNotification.php deleted file mode 100644 index 03b52e93d6..0000000000 --- a/app/Notifications/Admin/TestNotification.php +++ /dev/null @@ -1,114 +0,0 @@ -. - */ - -declare(strict_types=1); - -namespace FireflyIII\Notifications\Admin; - -use FireflyIII\Support\Notifications\UrlValidator; -use Illuminate\Bus\Queueable; -use Illuminate\Notifications\Messages\MailMessage; -use Illuminate\Notifications\Messages\SlackMessage; -use Illuminate\Notifications\Notification; - -/** - * Class TestNotification - */ -class TestNotification extends Notification -{ - use Queueable; - - private string $address; - - /** - * Create a new notification instance. - */ - public function __construct(string $address) - { - $this->address = $address; - } - - /** - * Get the array representation of the notification. - * - * @param mixed $notifiable - * - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - * - * @return array - */ - public function toArray($notifiable) - { - return [ - ]; - } - - /** - * Get the mail representation of the notification. - * - * @param mixed $notifiable - * - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - * - * @return MailMessage - */ - public function toMail($notifiable) - { - return (new MailMessage()) - ->markdown('emails.admin-test', ['email' => $this->address]) - ->subject((string)trans('email.admin_test_subject')) - ; - } - - /** - * Get the Slack representation of the notification. - * - * @param mixed $notifiable - * - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - * - * @return SlackMessage - */ - public function toSlack($notifiable) - { - return (new SlackMessage())->content((string)trans('email.admin_test_subject')); - } - - /** - * Get the notification's delivery channels. - * - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - * - * @param mixed $notifiable - * - * @return array - */ - public function via($notifiable) - { - $slackUrl = app('fireflyconfig')->get('slack_webhook_url', '')->data; - if (UrlValidator::isValidWebhookURL($slackUrl)) { - return ['mail', 'slack']; - } - - return ['mail']; - } -} diff --git a/app/Notifications/Test/TestNotificationDiscord.php b/app/Notifications/Test/TestNotificationNtfy.php similarity index 78% rename from app/Notifications/Test/TestNotificationDiscord.php rename to app/Notifications/Test/TestNotificationNtfy.php index 603f1f2561..6131ae692d 100644 --- a/app/Notifications/Test/TestNotificationDiscord.php +++ b/app/Notifications/Test/TestNotificationNtfy.php @@ -28,11 +28,15 @@ use Illuminate\Bus\Queueable; use Illuminate\Notifications\Messages\MailMessage; use Illuminate\Notifications\Messages\SlackMessage; use Illuminate\Notifications\Notification; +use Ntfy\Message; +use Wijourdil\NtfyNotificationChannel\Channels\NtfyChannel; + +//use Illuminate\Notifications\Slack\SlackMessage; /** * Class TestNotification */ -class TestNotificationDiscord extends Notification +class TestNotificationNtfy extends Notification { use Queueable; @@ -61,6 +65,18 @@ class TestNotificationDiscord extends Notification ]; } + + public function toNtfy(mixed $notifiable): Message + { + $message = new Message(); + $message->topic(config('ntfy-notification-channel.topic')); + $message->title((string)trans('email.admin_test_subject')); + $message->body((string)trans('email.admin_test_message', ['channel' => 'ntfy'])); + $message->tags(['white_check_mark', 'ok_hand']); + + return $message; + } + /** * Get the mail representation of the notification. * @@ -83,13 +99,6 @@ class TestNotificationDiscord extends Notification * */ public function toSlack($notifiable) { - - // since it's an admin notificaiton, grab the URL from fireflyconfig - $url = app('fireflyconfig')->get('discord_webhook_url', '')->data; - - return (new SlackMessage()) - ->content((string)trans('email.admin_test_subject')) - ->to($url); } /** @@ -103,6 +112,6 @@ class TestNotificationDiscord extends Notification */ public function via($notifiable) { - return ['slack']; + return [NtfyChannel::class]; } } diff --git a/app/User.php b/app/User.php index fa79d3a12e..32f8346113 100644 --- a/app/User.php +++ b/app/User.php @@ -50,7 +50,6 @@ use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\UserGroup; use FireflyIII\Models\UserRole; use FireflyIII\Models\Webhook; -use FireflyIII\Notifications\Admin\TestNotification; use FireflyIII\Notifications\Admin\UserInvitation; use FireflyIII\Notifications\Admin\UserRegistration; use FireflyIII\Notifications\Admin\VersionCheckResult; diff --git a/composer.json b/composer.json index c85437acdc..c4f2819234 100644 --- a/composer.json +++ b/composer.json @@ -110,7 +110,8 @@ "spatie/period": "^2.4", "symfony/expression-language": "^7.0", "symfony/http-client": "^7.1", - "symfony/mailgun-mailer": "^7.1" + "symfony/mailgun-mailer": "^7.1", + "wijourdil/ntfy-notification-channel": "^3.0" }, "require-dev": { "barryvdh/laravel-debugbar": "^3.9", diff --git a/composer.lock b/composer.lock index 79b33dcb29..247d762df3 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "f813653aac7be9e344fb4ca91513df61", + "content-hash": "a2800d2f481f640d8cd058b3b31e8962", "packages": [ { "name": "bacon/bacon-qr-code", @@ -7033,6 +7033,66 @@ ], "time": "2024-06-12T15:01:18+00:00" }, + { + "name": "spatie/laravel-package-tools", + "version": "1.16.6", + "source": { + "type": "git", + "url": "https://github.com/spatie/laravel-package-tools.git", + "reference": "1f26942dc1e5c49eacfced34fdbc29ed234bd7b3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/laravel-package-tools/zipball/1f26942dc1e5c49eacfced34fdbc29ed234bd7b3", + "reference": "1f26942dc1e5c49eacfced34fdbc29ed234bd7b3", + "shasum": "" + }, + "require": { + "illuminate/contracts": "^9.28|^10.0|^11.0", + "php": "^8.0" + }, + "require-dev": { + "mockery/mockery": "^1.5", + "orchestra/testbench": "^7.7|^8.0", + "pestphp/pest": "^1.22", + "phpunit/phpunit": "^9.5.24", + "spatie/pest-plugin-test-time": "^1.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Spatie\\LaravelPackageTools\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Freek Van der Herten", + "email": "freek@spatie.be", + "role": "Developer" + } + ], + "description": "Tools for creating Laravel packages", + "homepage": "https://github.com/spatie/laravel-package-tools", + "keywords": [ + "laravel-package-tools", + "spatie" + ], + "support": { + "issues": "https://github.com/spatie/laravel-package-tools/issues", + "source": "https://github.com/spatie/laravel-package-tools/tree/1.16.6" + }, + "funding": [ + { + "url": "https://github.com/spatie", + "type": "github" + } + ], + "time": "2024-11-18T15:02:02+00:00" + }, { "name": "spatie/period", "version": "2.4.0", @@ -10093,6 +10153,145 @@ ], "time": "2024-09-25T14:20:29+00:00" }, + { + "name": "thecodingmachine/safe", + "version": "v2.5.0", + "source": { + "type": "git", + "url": "https://github.com/thecodingmachine/safe.git", + "reference": "3115ecd6b4391662b4931daac4eba6b07a2ac1f0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thecodingmachine/safe/zipball/3115ecd6b4391662b4931daac4eba6b07a2ac1f0", + "reference": "3115ecd6b4391662b4931daac4eba6b07a2ac1f0", + "shasum": "" + }, + "require": { + "php": "^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.5", + "phpunit/phpunit": "^9.5", + "squizlabs/php_codesniffer": "^3.2", + "thecodingmachine/phpstan-strict-rules": "^1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.2.x-dev" + } + }, + "autoload": { + "files": [ + "deprecated/apc.php", + "deprecated/array.php", + "deprecated/datetime.php", + "deprecated/libevent.php", + "deprecated/misc.php", + "deprecated/password.php", + "deprecated/mssql.php", + "deprecated/stats.php", + "deprecated/strings.php", + "lib/special_cases.php", + "deprecated/mysqli.php", + "generated/apache.php", + "generated/apcu.php", + "generated/array.php", + "generated/bzip2.php", + "generated/calendar.php", + "generated/classobj.php", + "generated/com.php", + "generated/cubrid.php", + "generated/curl.php", + "generated/datetime.php", + "generated/dir.php", + "generated/eio.php", + "generated/errorfunc.php", + "generated/exec.php", + "generated/fileinfo.php", + "generated/filesystem.php", + "generated/filter.php", + "generated/fpm.php", + "generated/ftp.php", + "generated/funchand.php", + "generated/gettext.php", + "generated/gmp.php", + "generated/gnupg.php", + "generated/hash.php", + "generated/ibase.php", + "generated/ibmDb2.php", + "generated/iconv.php", + "generated/image.php", + "generated/imap.php", + "generated/info.php", + "generated/inotify.php", + "generated/json.php", + "generated/ldap.php", + "generated/libxml.php", + "generated/lzf.php", + "generated/mailparse.php", + "generated/mbstring.php", + "generated/misc.php", + "generated/mysql.php", + "generated/network.php", + "generated/oci8.php", + "generated/opcache.php", + "generated/openssl.php", + "generated/outcontrol.php", + "generated/pcntl.php", + "generated/pcre.php", + "generated/pgsql.php", + "generated/posix.php", + "generated/ps.php", + "generated/pspell.php", + "generated/readline.php", + "generated/rpminfo.php", + "generated/rrd.php", + "generated/sem.php", + "generated/session.php", + "generated/shmop.php", + "generated/sockets.php", + "generated/sodium.php", + "generated/solr.php", + "generated/spl.php", + "generated/sqlsrv.php", + "generated/ssdeep.php", + "generated/ssh2.php", + "generated/stream.php", + "generated/strings.php", + "generated/swoole.php", + "generated/uodbc.php", + "generated/uopz.php", + "generated/url.php", + "generated/var.php", + "generated/xdiff.php", + "generated/xml.php", + "generated/xmlrpc.php", + "generated/yaml.php", + "generated/yaz.php", + "generated/zip.php", + "generated/zlib.php" + ], + "classmap": [ + "lib/DateTime.php", + "lib/DateTimeImmutable.php", + "lib/Exceptions/", + "deprecated/Exceptions/", + "generated/Exceptions/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHP core functions that throw exceptions instead of returning FALSE on error", + "support": { + "issues": "https://github.com/thecodingmachine/safe/issues", + "source": "https://github.com/thecodingmachine/safe/tree/v2.5.0" + }, + "time": "2023-04-05T11:54:14+00:00" + }, { "name": "tijsverkoyen/css-to-inline-styles", "version": "v2.2.7", @@ -10225,6 +10424,58 @@ ], "time": "2024-11-17T15:59:19+00:00" }, + { + "name": "verifiedjoseph/ntfy-php-library", + "version": "v4.6.2", + "source": { + "type": "git", + "url": "https://github.com/VerifiedJoseph/ntfy-php-library.git", + "reference": "0e5c22be5e1633534e41a5835a8975bec5ff4cef" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/VerifiedJoseph/ntfy-php-library/zipball/0e5c22be5e1633534e41a5835a8975bec5ff4cef", + "reference": "0e5c22be5e1633534e41a5835a8975bec5ff4cef", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "ext-json": "*", + "guzzlehttp/guzzle": "^7.4", + "php": "^8.1" + }, + "require-dev": { + "phpstan/phpstan": "^1.0", + "phpunit/phpunit": "^10.5", + "squizlabs/php_codesniffer": "^3.7" + }, + "type": "library", + "autoload": { + "psr-4": { + "Ntfy\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "VerifiedJoseph", + "homepage": "https://github.com/VerifiedJoseph" + } + ], + "description": "PHP library for interacting with a Ntfy server", + "homepage": "https://github.com/VerifiedJoseph/ntfy-php-library", + "keywords": [ + "Ntfy" + ], + "support": { + "issues": "https://github.com/VerifiedJoseph/ntfy-php-library/issues", + "source": "https://github.com/VerifiedJoseph/ntfy-php-library/tree/v4.6.2" + }, + "time": "2024-08-01T12:43:09+00:00" + }, { "name": "vlucas/phpdotenv", "version": "v5.6.1", @@ -10440,6 +10691,81 @@ "source": "https://github.com/webmozarts/assert/tree/1.11.0" }, "time": "2022-06-03T18:03:27+00:00" + }, + { + "name": "wijourdil/ntfy-notification-channel", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/wijourdil/ntfy-notification-channel.git", + "reference": "508ad1f0e1852b0bed966e9ebbf6f760ca2bac7b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/wijourdil/ntfy-notification-channel/zipball/508ad1f0e1852b0bed966e9ebbf6f760ca2bac7b", + "reference": "508ad1f0e1852b0bed966e9ebbf6f760ca2bac7b", + "shasum": "" + }, + "require": { + "illuminate/contracts": "^11.0", + "php": "^8.2", + "spatie/laravel-package-tools": "^1.13.0", + "thecodingmachine/safe": "^2.4", + "verifiedjoseph/ntfy-php-library": "^4.0", + "webmozart/assert": "^1.11" + }, + "require-dev": { + "larastan/larastan": "^2.0.1", + "laravel/pint": "^1.0", + "nunomaduro/collision": "^8.1", + "orchestra/testbench": "^9.0", + "phpstan/phpstan-deprecation-rules": "^1.0", + "phpstan/phpstan-phpunit": "^1.0", + "phpstan/phpstan-webmozart-assert": "^1.2", + "phpunit/phpunit": "^10.1", + "thecodingmachine/phpstan-safe-rule": "^1.2" + }, + "type": "library", + "extra": { + "laravel": { + "aliases": { + "NtfyNotificationChannel": "Wijourdil\\NtfyNotificationChannel\\Facades\\NtfyNotificationChannel" + }, + "providers": [ + "Wijourdil\\NtfyNotificationChannel\\NtfyNotificationChannelServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Wijourdil\\NtfyNotificationChannel\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Wilfried Jourdil", + "email": "wijourdil@protonmail.com", + "role": "Developer" + } + ], + "description": "ntfy.sh Notification Channel for Laravel", + "homepage": "https://github.com/wijourdil/ntfy-notification-channel", + "keywords": [ + "laravel", + "notification", + "ntfy-notification-channel", + "ntfy.sh", + "wijourdil" + ], + "support": { + "issues": "https://github.com/wijourdil/ntfy-notification-channel/issues", + "source": "https://github.com/wijourdil/ntfy-notification-channel/tree/3.0.0" + }, + "time": "2024-04-17T12:37:09+00:00" } ], "packages-dev": [ diff --git a/config/notifications.php b/config/notifications.php index 394ad38c32..a5f728e4fc 100644 --- a/config/notifications.php +++ b/config/notifications.php @@ -24,7 +24,7 @@ return [ 'channels' => [ 'email' => ['enabled' => true, 'ui_configurable' => 0,], 'slack' => ['enabled' => true, 'ui_configurable' => 1,], - 'nfty' => ['enabled' => false, 'ui_configurable' => 0,], + 'ntfy' => ['enabled' => true, 'ui_configurable' => 0,], 'pushover' => ['enabled' => false, 'ui_configurable' => 0,], 'gotify' => ['enabled' => false, 'ui_configurable' => 0,], 'pushbullet' => ['enabled' => false, 'ui_configurable' => 0,], diff --git a/config/ntfy-notification-channel.php b/config/ntfy-notification-channel.php new file mode 100644 index 0000000000..e1ca641b7f --- /dev/null +++ b/config/ntfy-notification-channel.php @@ -0,0 +1,14 @@ + env('NTFY_SERVER', 'https://ntfy.sh'), + 'topic' => env('NTFY_TOPIC', ''), + 'authentication' => [ + 'enabled' => (bool) env('NTFY_AUTH_ENABLED', false), + 'username' => env('NTFY_AUTH_USERNAME', ''), + 'password' => env('NTFY_AUTH_PASSWORD', ''), + ], + +]; diff --git a/resources/lang/en_US/email.php b/resources/lang/en_US/email.php index e805aaae61..be6a1089c5 100644 --- a/resources/lang/en_US/email.php +++ b/resources/lang/en_US/email.php @@ -34,6 +34,7 @@ return [ // admin test 'admin_test_subject' => 'A test message from your Firefly III installation', 'admin_test_body' => 'This is a test message from your Firefly III instance. It was sent to :email.', + 'admin_test_message' => 'This is a test message from your Firefly III instance over channel ":channel".', // Ignore this comment diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php index a427410ada..b168ca5195 100644 --- a/resources/lang/en_US/firefly.php +++ b/resources/lang/en_US/firefly.php @@ -2500,8 +2500,7 @@ return [ 'available_channels_expl' => 'These channels are available to send notifications over. To test your confiuration, use the buttons below. Please note that the buttons have no spam control.', 'notification_channel_name_email' => 'Email', 'notification_channel_name_slack' => 'Slack', - 'notification_channel_name_discord' => 'Discord', - 'notification_channel_name_nfty' => 'Nfty', + 'notification_channel_name_ntfy' => 'Ntfy.sh', 'notification_channel_name_pushover' => 'Pushover', 'notification_channel_name_gotify' => 'Gotify', 'notification_channel_name_pushbullet' => 'Pushbullet', @@ -2509,7 +2508,7 @@ return [ 'configure_channel_in_env' => 'needs environment variables', 'test_notification_channel_name_email' => 'Test email', 'test_notification_channel_name_slack' => 'Test Slack', - 'test_notification_channel_name_discord' => 'Test Discord', + 'test_notification_channel_name_ntfy' => 'Test Ntfy.sh', 'split_transaction_title' => 'Description of the split transaction', 'split_transaction_title_help' => 'If you create a split transaction, there must be a global description for all splits of the transaction.', diff --git a/resources/views/admin/notifications/index.twig b/resources/views/admin/notifications/index.twig index d32fb3b610..76e372cf1e 100644 --- a/resources/views/admin/notifications/index.twig +++ b/resources/views/admin/notifications/index.twig @@ -48,11 +48,11 @@