From 05f3d4af6187c2971f48c2a7a77f5dc09e8a04b0 Mon Sep 17 00:00:00 2001 From: svenvg93 Date: Tue, 11 Jun 2024 21:55:32 +0200 Subject: [PATCH 1/4] First Commit --- .../SendSlackTestNotification.php | 39 ++++++ .../Pages/Settings/NotificationPage.php | 44 ++++++ .../SendSpeedtestCompletedNotification.php | 58 ++++++++ .../SendSpeedtestThresholdNotification.php | 132 ++++++++++++++++++ app/Settings/NotificationSettings.php | 8 ++ ...650_create_slack_notification_settings.php | 14 ++ .../views/slack/speedtest-completed.blade.php | 12 ++ .../views/slack/speedtest-threshold.blade.php | 8 ++ 8 files changed, 315 insertions(+) create mode 100644 app/Actions/Notifications/SendSlackTestNotification.php create mode 100644 app/Listeners/Slack/SendSpeedtestCompletedNotification.php create mode 100644 app/Listeners/Slack/SendSpeedtestThresholdNotification.php create mode 100644 database/settings/2024_02_22_144650_create_slack_notification_settings.php create mode 100644 resources/views/slack/speedtest-completed.blade.php create mode 100644 resources/views/slack/speedtest-threshold.blade.php diff --git a/app/Actions/Notifications/SendSlackTestNotification.php b/app/Actions/Notifications/SendSlackTestNotification.php new file mode 100644 index 000000000..077eb0b1c --- /dev/null +++ b/app/Actions/Notifications/SendSlackTestNotification.php @@ -0,0 +1,39 @@ +title('You need to add Slack URLs!') + ->warning() + ->send(); + + return; + } + + foreach ($webhooks as $webhook) { + WebhookCall::create() + ->url($webhook['url']) + ->payload(["text" => "👋 Testing the Slack notification channel."]) // Changed ":" to "=>" + ->doNotSign() + ->dispatch(); + } + + Notification::make() + ->title('Test Slack notification sent.') + ->success() + ->send(); + } + +} diff --git a/app/Filament/Pages/Settings/NotificationPage.php b/app/Filament/Pages/Settings/NotificationPage.php index a40a5e080..bae0a5ffc 100755 --- a/app/Filament/Pages/Settings/NotificationPage.php +++ b/app/Filament/Pages/Settings/NotificationPage.php @@ -4,6 +4,7 @@ use App\Actions\Notifications\SendDatabaseTestNotification; use App\Actions\Notifications\SendDiscordTestNotification; +use App\Actions\Notifications\SendSlackTestNotification; use App\Actions\Notifications\SendMailTestNotification; use App\Actions\Notifications\SendTelegramTestNotification; use App\Actions\Notifications\SendWebhookTestNotification; @@ -127,6 +128,49 @@ public function form(Form $form): Form 'md' => 2, ]), + Forms\Components\Section::make('Slack') + ->schema([ + Forms\Components\Toggle::make('slack_enabled') + ->label('Enable Slack webhook notifications') + ->reactive() + ->columnSpanFull(), + Forms\Components\Grid::make([ + 'default' => 1, + ]) + ->hidden(fn (Forms\Get $get) => $get('slack_enabled') !== true) + ->schema([ + Forms\Components\Fieldset::make('Triggers') + ->schema([ + Forms\Components\Toggle::make('slack_on_speedtest_run') + ->label('Notify on every speedtest run') + ->columnSpanFull(), + Forms\Components\Toggle::make('slack_on_threshold_failure') + ->label('Notify on threshold failures') + ->columnSpanFull(), + ]), + Forms\Components\Repeater::make('slack_webhooks') + ->label('Webhooks') + ->schema([ + Forms\Components\TextInput::make('url') + ->maxLength(2000) + ->required() + ->url(), + ]) + ->columnSpanFull(), + Forms\Components\Actions::make([ + Forms\Components\Actions\Action::make('test Slack') + ->label('Test slack webhook') + ->action(fn (Forms\Get $get) => SendSlackTestNotification::run(webhooks: $get('slack_webhooks'))) + ->hidden(fn (Forms\Get $get) => ! count($get('slack_webhooks'))), + ]), + ]), + ]) + ->compact() + ->columns([ + 'default' => 1, + 'md' => 2, + ]), + Forms\Components\Section::make('Mail') ->schema([ Forms\Components\Toggle::make('mail_enabled') diff --git a/app/Listeners/Slack/SendSpeedtestCompletedNotification.php b/app/Listeners/Slack/SendSpeedtestCompletedNotification.php new file mode 100644 index 000000000..d56362759 --- /dev/null +++ b/app/Listeners/Slack/SendSpeedtestCompletedNotification.php @@ -0,0 +1,58 @@ +slack_enabled) { + return; + } + + if (! $notificationSettings->slack_on_speedtest_run) { + return; + } + + if (! count($notificationSettings->slack_webhooks)) { + Log::warning('Slack URLs not found, check Slack notification channel settings.'); + + return; + } + + $payload = [ + 'text' => view('slack.speedtest-completed', [ + 'id' => $event->result->id, + 'service' => Str::title($event->result->service), + 'serverName' => $event->result->server_name, + 'serverId' => $event->result->server_id, + 'isp' => $event->result->isp, + 'ping' => round($event->result->ping).' ms', + 'download' => Number::toBitRate(bits: $event->result->download_bits, precision: 2), + 'upload' => Number::toBitRate(bits: $event->result->upload_bits, precision: 2), + 'packetLoss' => $event->result->packet_loss, + 'url' => url('/admin/results'), + ])->render(), + ]; + + foreach ($notificationSettings->slack_webhooks as $url) { + WebhookCall::create() + ->url($url['url']) + ->payload($payload) + ->doNotSign() + ->dispatch(); + } + } +} diff --git a/app/Listeners/Slack/SendSpeedtestThresholdNotification.php b/app/Listeners/Slack/SendSpeedtestThresholdNotification.php new file mode 100644 index 000000000..b3eb7924e --- /dev/null +++ b/app/Listeners/Slack/SendSpeedtestThresholdNotification.php @@ -0,0 +1,132 @@ +slack_enabled) { + return; + } + + if (! $notificationSettings->slack_on_threshold_failure) { + return; + } + + if (! count($notificationSettings->slack_webhooks)) { + Log::warning('Slack urls not found, check Slack notification channel settings.'); + + return; + } + + $thresholdSettings = new ThresholdSettings(); + + if (! $thresholdSettings->absolute_enabled) { + return; + } + + $failed = []; + + if ($thresholdSettings->absolute_download > 0) { + array_push($failed, $this->absoluteDownloadThreshold(event: $event, thresholdSettings: $thresholdSettings)); + } + + if ($thresholdSettings->absolute_upload > 0) { + array_push($failed, $this->absoluteUploadThreshold(event: $event, thresholdSettings: $thresholdSettings)); + } + + if ($thresholdSettings->absolute_ping > 0) { + array_push($failed, $this->absolutePingThreshold(event: $event, thresholdSettings: $thresholdSettings)); + } + + $failed = array_filter($failed); + + if (! count($failed)) { + Log::warning('Failed Slack thresholds not found, won\'t send notification.'); + + return; + } + + $payload = [ + 'text' => view('slack.speedtest-threshold', [ + 'id' => $event->result->id, + 'service' => Str::title($event->result->service), + 'serverName' => $event->result->server_name, + 'serverId' => $event->result->server_id, + 'isp' => $event->result->isp, + 'metrics' => $failed, + 'url' => url('/admin/results'), + ])->render(), + ]; + + foreach ($notificationSettings->slack_webhooks as $url) { + WebhookCall::create() + ->url($url['url']) + ->payload($payload) + ->doNotSign() + ->dispatch(); + } + } + + /** + * Build Slack notification if absolute download threshold is breached. + */ + protected function absoluteDownloadThreshold(SpeedtestCompleted $event, ThresholdSettings $thresholdSettings): bool|array + { + if (! absoluteDownloadThresholdFailed($thresholdSettings->absolute_download, $event->result->download)) { + return false; + } + + return [ + 'name' => 'Download', + 'threshold' => $thresholdSettings->absolute_download.' Mbps', + 'value' => Number::toBitRate(bits: $event->result->download_bits, precision: 2), + ]; + } + + /** + * Build Slack notification if absolute upload threshold is breached. + */ + protected function absoluteUploadThreshold(SpeedtestCompleted $event, ThresholdSettings $thresholdSettings): bool|array + { + if (! absoluteUploadThresholdFailed($thresholdSettings->absolute_upload, $event->result->upload)) { + return false; + } + + return [ + 'name' => 'Upload', + 'threshold' => $thresholdSettings->absolute_upload.' Mbps', + 'value' => Number::toBitRate(bits: $event->result->upload_bits, precision: 2), + ]; + } + + /** + * Build Slack notification if absolute ping threshold is breached. + */ + protected function absolutePingThreshold(SpeedtestCompleted $event, ThresholdSettings $thresholdSettings): bool|array + { + if (! absolutePingThresholdFailed($thresholdSettings->absolute_ping, $event->result->ping)) { + return false; + } + + return [ + 'name' => 'Ping', + 'threshold' => $thresholdSettings->absolute_ping.' ms', + 'value' => round($event->result->ping, 2).' ms', + ]; + } +} diff --git a/app/Settings/NotificationSettings.php b/app/Settings/NotificationSettings.php index ff2d4d22a..830514be4 100644 --- a/app/Settings/NotificationSettings.php +++ b/app/Settings/NotificationSettings.php @@ -46,6 +46,14 @@ class NotificationSettings extends Settings public ?array $discord_webhooks; + public bool $slack_enabled; + + public bool $slack_on_speedtest_run; + + public bool $slack_on_threshold_failure; + + public ?array $slack_webhooks; + public static function group(): string { return 'notification'; diff --git a/database/settings/2024_02_22_144650_create_slack_notification_settings.php b/database/settings/2024_02_22_144650_create_slack_notification_settings.php new file mode 100644 index 000000000..755a70b74 --- /dev/null +++ b/database/settings/2024_02_22_144650_create_slack_notification_settings.php @@ -0,0 +1,14 @@ +migrator->add('notification.slack_enabled', false); + $this->migrator->add('notification.slack_on_speedtest_run', false); + $this->migrator->add('notification.slack_on_threshold_failure', false); + $this->migrator->add('notification.slack_webhooks', null); + } +}; diff --git a/resources/views/slack/speedtest-completed.blade.php b/resources/views/slack/speedtest-completed.blade.php new file mode 100644 index 000000000..b10afe419 --- /dev/null +++ b/resources/views/slack/speedtest-completed.blade.php @@ -0,0 +1,12 @@ +*Speedtest Completed - #{{ $id }}* + +A new speedtest was completed using *{{ $service }}*. + +- *Server name:* {{ $serverName }} +- *Server ID:* {{ $serverId }} +- *ISP:* {{ $isp }} +- *Ping:* {{ $ping }} +- *Download:* {{ $download }} +- *Upload:* {{ $upload }} +- *Packet Loss:* {{ $packetLoss }} *%* +- *URL:* {{ $url }} diff --git a/resources/views/slack/speedtest-threshold.blade.php b/resources/views/slack/speedtest-threshold.blade.php new file mode 100644 index 000000000..2cb708643 --- /dev/null +++ b/resources/views/slack/speedtest-threshold.blade.php @@ -0,0 +1,8 @@ +**Speedtest Threshold Breached - #{{ $id }}** + +A new speedtest was completed using **{{ $service }}** on **{{ $isp }}** but a threshold was breached. + +@foreach ($metrics as $item) +- **{{ $item['name'] }}** {{ $item['threshold'] }}: {{ $item['value'] }} +@endforeach +- **URL:** {{ $url }} From fa6f3bdc3f4dd30c8b04051287453fdbe59dea31 Mon Sep 17 00:00:00 2001 From: svenvg93 Date: Wed, 12 Jun 2024 17:49:31 +0200 Subject: [PATCH 2/4] Fix lint --- .../Notifications/SendSlackTestNotification.php | 12 +++++------- app/Filament/Pages/Settings/NotificationPage.php | 4 ++-- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/app/Actions/Notifications/SendSlackTestNotification.php b/app/Actions/Notifications/SendSlackTestNotification.php index 077eb0b1c..95db18439 100644 --- a/app/Actions/Notifications/SendSlackTestNotification.php +++ b/app/Actions/Notifications/SendSlackTestNotification.php @@ -2,7 +2,6 @@ namespace App\Actions\Notifications; - use Filament\Notifications\Notification; use Lorisleiva\Actions\Concerns\AsAction; use Spatie\WebhookServer\WebhookCall; @@ -18,22 +17,21 @@ public function handle(array $webhooks) ->title('You need to add Slack URLs!') ->warning() ->send(); - + return; } - + foreach ($webhooks as $webhook) { WebhookCall::create() ->url($webhook['url']) - ->payload(["text" => "👋 Testing the Slack notification channel."]) // Changed ":" to "=>" + ->payload(['text' => '👋 Testing the Slack notification channel.']) ->doNotSign() ->dispatch(); } - + Notification::make() ->title('Test Slack notification sent.') ->success() ->send(); - } - + } } diff --git a/app/Filament/Pages/Settings/NotificationPage.php b/app/Filament/Pages/Settings/NotificationPage.php index bae0a5ffc..7d12fff11 100755 --- a/app/Filament/Pages/Settings/NotificationPage.php +++ b/app/Filament/Pages/Settings/NotificationPage.php @@ -4,8 +4,8 @@ use App\Actions\Notifications\SendDatabaseTestNotification; use App\Actions\Notifications\SendDiscordTestNotification; -use App\Actions\Notifications\SendSlackTestNotification; use App\Actions\Notifications\SendMailTestNotification; +use App\Actions\Notifications\SendSlackTestNotification; use App\Actions\Notifications\SendTelegramTestNotification; use App\Actions\Notifications\SendWebhookTestNotification; use App\Settings\NotificationSettings; @@ -128,7 +128,7 @@ public function form(Form $form): Form 'md' => 2, ]), - Forms\Components\Section::make('Slack') + Forms\Components\Section::make('Slack') ->schema([ Forms\Components\Toggle::make('slack_enabled') ->label('Enable Slack webhook notifications') From 74b5e116465f0c2aaf6f63d33f40af7f9ccbd836 Mon Sep 17 00:00:00 2001 From: svenvg93 Date: Tue, 25 Jun 2024 00:30:18 +0200 Subject: [PATCH 3/4] add_url_placeholder --- app/Filament/Pages/Settings/NotificationPage.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/Filament/Pages/Settings/NotificationPage.php b/app/Filament/Pages/Settings/NotificationPage.php index 7d12fff11..b83bbe032 100755 --- a/app/Filament/Pages/Settings/NotificationPage.php +++ b/app/Filament/Pages/Settings/NotificationPage.php @@ -152,6 +152,7 @@ public function form(Form $form): Form ->label('Webhooks') ->schema([ Forms\Components\TextInput::make('url') + ->placeholder('https://hooks.slack.com/services/abc/xyz') ->maxLength(2000) ->required() ->url(), From 82de6d7a479fe37d2accad39ff45f4f6021b72ec Mon Sep 17 00:00:00 2001 From: svenvg93 Date: Tue, 25 Jun 2024 18:13:40 +0200 Subject: [PATCH 4/4] Fix the liniting --- app/Filament/Pages/Settings/NotificationPage.php | 6 +++--- app/Settings/NotificationSettings.php | 1 - 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/app/Filament/Pages/Settings/NotificationPage.php b/app/Filament/Pages/Settings/NotificationPage.php index 1b1b0a3d0..c11e56d54 100755 --- a/app/Filament/Pages/Settings/NotificationPage.php +++ b/app/Filament/Pages/Settings/NotificationPage.php @@ -173,8 +173,8 @@ public function form(Form $form): Form 'default' => 1, 'md' => 2, ]), - - Forms\Components\Section::make('Slack') + + Forms\Components\Section::make('Slack') ->schema([ Forms\Components\Toggle::make('slack_enabled') ->label('Enable Slack webhook notifications') @@ -216,7 +216,7 @@ public function form(Form $form): Form ->columns([ 'default' => 1, 'md' => 2, - ]), + ]), Forms\Components\Section::make('Mail') ->schema([ diff --git a/app/Settings/NotificationSettings.php b/app/Settings/NotificationSettings.php index ad37c2eb3..4c8063799 100644 --- a/app/Settings/NotificationSettings.php +++ b/app/Settings/NotificationSettings.php @@ -62,7 +62,6 @@ class NotificationSettings extends Settings public ?array $gotify_webhooks; - public static function group(): string { return 'notification';