Skip to content

Commit 2830aad

Browse files
Add Pushover Notifications via Webhooks (#1574)
* add_pushover * add_placeholder * Linting --------- Co-authored-by: Alex Justesen <[email protected]>
1 parent 987b099 commit 2830aad

File tree

8 files changed

+339
-0
lines changed

8 files changed

+339
-0
lines changed
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?php
2+
3+
namespace App\Actions\Notifications;
4+
5+
use Filament\Notifications\Notification;
6+
use Lorisleiva\Actions\Concerns\AsAction;
7+
use Spatie\WebhookServer\WebhookCall;
8+
9+
class SendPushoverTestNotification
10+
{
11+
use AsAction;
12+
13+
public function handle(array $webhooks)
14+
{
15+
if (! count($webhooks)) {
16+
Notification::make()
17+
->title('You need to add Pushover URLs!')
18+
->warning()
19+
->send();
20+
21+
return;
22+
}
23+
24+
foreach ($webhooks as $webhook) {
25+
WebhookCall::create()
26+
->url($webhook['url'])
27+
->payload([
28+
'token' => $webhook['api_token'],
29+
'user' => $webhook['user_key'],
30+
'message' => '👋 Testing the Pushover notification channel.',
31+
])
32+
->doNotSign()
33+
->dispatch();
34+
}
35+
36+
Notification::make()
37+
->title('Test Pushover notification sent.')
38+
->success()
39+
->send();
40+
}
41+
}

app/Filament/Pages/Settings/NotificationPage.php

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use App\Actions\Notifications\SendGotifyTestNotification;
88
use App\Actions\Notifications\SendHealthCheckTestNotification;
99
use App\Actions\Notifications\SendMailTestNotification;
10+
use App\Actions\Notifications\SendPushoverTestNotification;
1011
use App\Actions\Notifications\SendSlackTestNotification;
1112
use App\Actions\Notifications\SendTelegramTestNotification;
1213
use App\Actions\Notifications\SendWebhookTestNotification;
@@ -87,6 +88,63 @@ public function form(Form $form): Form
8788
'md' => 2,
8889
]),
8990

91+
Forms\Components\Section::make('Pushover')
92+
->schema([
93+
Forms\Components\Toggle::make('pushover_enabled')
94+
->label('Enable Pushover webhook notifications')
95+
->reactive()
96+
->columnSpanFull(),
97+
Forms\Components\Grid::make([
98+
'default' => 1,
99+
])
100+
->hidden(fn (Forms\Get $get) => $get('pushover_enabled') !== true)
101+
->schema([
102+
Forms\Components\Fieldset::make('Triggers')
103+
->schema([
104+
Forms\Components\Toggle::make('pushover_on_speedtest_run')
105+
->label('Notify on every speedtest run')
106+
->columnSpanFull(),
107+
Forms\Components\Toggle::make('pushover_on_threshold_failure')
108+
->label('Notify on threshold failures')
109+
->columnSpanFull(),
110+
]),
111+
Forms\Components\Repeater::make('pushover_webhooks')
112+
->label('Pushover Webhooks')
113+
->schema([
114+
Forms\Components\TextInput::make('url')
115+
->label('URL')
116+
->placeholder('http://api.pushover.net/1/messages.json')
117+
->maxLength(2000)
118+
->required()
119+
->url(),
120+
Forms\Components\TextInput::make('user_key')
121+
->label('User Key')
122+
->placeholder('Your Pushover User Key')
123+
->maxLength(200)
124+
->required(),
125+
Forms\Components\TextInput::make('api_token')
126+
->label('API Token')
127+
->placeholder('Your Pushover API Token')
128+
->maxLength(200)
129+
->required(),
130+
])
131+
->columnSpanFull(),
132+
Forms\Components\Actions::make([
133+
Forms\Components\Actions\Action::make('test pushover')
134+
->label('Test Pushover webhook')
135+
->action(fn (Forms\Get $get) => SendPushoverTestNotification::run(
136+
webhooks: $get('pushover_webhooks')
137+
))
138+
->hidden(fn (Forms\Get $get) => ! count($get('pushover_webhooks'))),
139+
]),
140+
]),
141+
])
142+
->compact()
143+
->columns([
144+
'default' => 1,
145+
'md' => 2,
146+
]),
147+
90148
Forms\Components\Section::make('Discord')
91149
->schema([
92150
Forms\Components\Toggle::make('discord_enabled')
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
<?php
2+
3+
namespace App\Listeners\Pushover;
4+
5+
use App\Events\SpeedtestCompleted;
6+
use App\Helpers\Number;
7+
use App\Settings\NotificationSettings;
8+
use Illuminate\Support\Facades\Log;
9+
use Illuminate\Support\Str;
10+
use Spatie\WebhookServer\WebhookCall;
11+
12+
class SendSpeedtestCompletedNotification
13+
{
14+
/**
15+
* Handle the event.
16+
*/
17+
public function handle(SpeedtestCompleted $event): void
18+
{
19+
$notificationSettings = new NotificationSettings();
20+
21+
if (! $notificationSettings->pushover_enabled) {
22+
return;
23+
}
24+
25+
if (! $notificationSettings->pushover_on_speedtest_run) {
26+
return;
27+
}
28+
29+
if (! count($notificationSettings->pushover_webhooks)) {
30+
Log::warning('Pushover urls not found, check Pushover notification channel settings.');
31+
32+
return;
33+
}
34+
35+
$payload = [
36+
view('pushover.speedtest-completed', [
37+
'id' => $event->result->id,
38+
'service' => Str::title($event->result->service),
39+
'serverName' => $event->result->server_name,
40+
'serverId' => $event->result->server_id,
41+
'isp' => $event->result->isp,
42+
'ping' => round($event->result->ping).' ms',
43+
'download' => Number::toBitRate(bits: $event->result->download_bits, precision: 2),
44+
'upload' => Number::toBitRate(bits: $event->result->upload_bits, precision: 2),
45+
'packetLoss' => $event->result->packet_loss,
46+
'url' => url('/admin/results'),
47+
])->render(),
48+
];
49+
50+
foreach ($notificationSettings->pushover_webhooks as $url) {
51+
WebhookCall::create()
52+
->url($url['url'])
53+
->payload([
54+
'token' => $url['api_token'],
55+
'user' => $url['user_key'],
56+
'message' => $payload,
57+
])
58+
->doNotSign()
59+
->dispatch();
60+
}
61+
}
62+
}
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
<?php
2+
3+
namespace App\Listeners\Pushover;
4+
5+
use App\Events\SpeedtestCompleted;
6+
use App\Helpers\Number;
7+
use App\Settings\NotificationSettings;
8+
use App\Settings\ThresholdSettings;
9+
use Illuminate\Support\Facades\Log;
10+
use Illuminate\Support\Str;
11+
use Spatie\WebhookServer\WebhookCall;
12+
13+
class SendSpeedtestThresholdNotification
14+
{
15+
/**
16+
* Handle the event.
17+
*/
18+
public function handle(SpeedtestCompleted $event): void
19+
{
20+
$notificationSettings = new NotificationSettings();
21+
22+
if (! $notificationSettings->pushover_enabled) {
23+
return;
24+
}
25+
26+
if (! $notificationSettings->pushover_on_threshold_failure) {
27+
return;
28+
}
29+
30+
if (! count($notificationSettings->pushover_webhooks)) {
31+
Log::warning('Pushover urls not found, check Pushover notification channel settings.');
32+
33+
return;
34+
}
35+
36+
$thresholdSettings = new ThresholdSettings();
37+
38+
if (! $thresholdSettings->absolute_enabled) {
39+
return;
40+
}
41+
42+
$failed = [];
43+
44+
if ($thresholdSettings->absolute_download > 0) {
45+
array_push($failed, $this->absoluteDownloadThreshold(event: $event, thresholdSettings: $thresholdSettings));
46+
}
47+
48+
if ($thresholdSettings->absolute_upload > 0) {
49+
array_push($failed, $this->absoluteUploadThreshold(event: $event, thresholdSettings: $thresholdSettings));
50+
}
51+
52+
if ($thresholdSettings->absolute_ping > 0) {
53+
array_push($failed, $this->absolutePingThreshold(event: $event, thresholdSettings: $thresholdSettings));
54+
}
55+
56+
$failed = array_filter($failed);
57+
58+
if (! count($failed)) {
59+
Log::warning('Failed Pushover thresholds not found, won\'t send notification.');
60+
61+
return;
62+
}
63+
64+
$payload = [
65+
view('pushover.speedtest-threshold', [
66+
'id' => $event->result->id,
67+
'service' => Str::title($event->result->service),
68+
'serverName' => $event->result->server_name,
69+
'serverId' => $event->result->server_id,
70+
'isp' => $event->result->isp,
71+
'metrics' => $failed,
72+
'url' => url('/admin/results'),
73+
])->render(),
74+
];
75+
76+
foreach ($notificationSettings->pushover_webhooks as $url) {
77+
WebhookCall::create()
78+
->url($url['url'])
79+
->payload([
80+
'token' => $url['api_token'],
81+
'user' => $url['user_key'],
82+
'message' => $payload,
83+
])
84+
->doNotSign()
85+
->dispatch();
86+
}
87+
}
88+
89+
/**
90+
* Build Pushover notification if absolute download threshold is breached.
91+
*/
92+
protected function absoluteDownloadThreshold(SpeedtestCompleted $event, ThresholdSettings $thresholdSettings): bool|array
93+
{
94+
if (! absoluteDownloadThresholdFailed($thresholdSettings->absolute_download, $event->result->download)) {
95+
return false;
96+
}
97+
98+
return [
99+
'name' => 'Download',
100+
'threshold' => $thresholdSettings->absolute_download.' Mbps',
101+
'value' => Number::toBitRate(bits: $event->result->download_bits, precision: 2),
102+
];
103+
}
104+
105+
/**
106+
* Build Pushover notification if absolute upload threshold is breached.
107+
*/
108+
protected function absoluteUploadThreshold(SpeedtestCompleted $event, ThresholdSettings $thresholdSettings): bool|array
109+
{
110+
if (! absoluteUploadThresholdFailed($thresholdSettings->absolute_upload, $event->result->upload)) {
111+
return false;
112+
}
113+
114+
return [
115+
'name' => 'Upload',
116+
'threshold' => $thresholdSettings->absolute_upload.' Mbps',
117+
'value' => Number::toBitRate(bits: $event->result->upload_bits, precision: 2),
118+
];
119+
}
120+
121+
/**
122+
* Build Pushover notification if absolute ping threshold is breached.
123+
*/
124+
protected function absolutePingThreshold(SpeedtestCompleted $event, ThresholdSettings $thresholdSettings): bool|array
125+
{
126+
if (! absolutePingThresholdFailed($thresholdSettings->absolute_ping, $event->result->ping)) {
127+
return false;
128+
}
129+
130+
return [
131+
'name' => 'Ping',
132+
'threshold' => $thresholdSettings->absolute_ping.' ms',
133+
'value' => round($event->result->ping, 2).' ms',
134+
];
135+
}
136+
}

app/Settings/NotificationSettings.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,14 @@ class NotificationSettings extends Settings
4646

4747
public ?array $discord_webhooks;
4848

49+
public bool $pushover_enabled;
50+
51+
public bool $pushover_on_speedtest_run;
52+
53+
public bool $pushover_on_threshold_failure;
54+
55+
public ?array $pushover_webhooks;
56+
4957
public bool $healthcheck_enabled;
5058

5159
public bool $healthcheck_on_speedtest_run;
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
3+
use Spatie\LaravelSettings\Migrations\SettingsMigration;
4+
5+
return new class extends SettingsMigration
6+
{
7+
public function up(): void
8+
{
9+
$this->migrator->add('notification.pushover_enabled', false);
10+
$this->migrator->add('notification.pushover_on_speedtest_run', false);
11+
$this->migrator->add('notification.pushover_on_threshold_failure', false);
12+
$this->migrator->add('notification.pushover_webhooks', null);
13+
}
14+
};
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
Speedtest Completed - #{{ $id }}
2+
3+
A new speedtest was completed using {{ $service }}.
4+
5+
- Server name: {{ $serverName }}
6+
- Server ID: {{ $serverId }}
7+
- ISP: {{ $isp }}
8+
- Ping: {{ $ping }}
9+
- Download: {{ $download }}
10+
- Upload: {{ $upload }}
11+
- Packet Loss: {{ $packetLoss }} %
12+
- URL: {{ $url }}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
Speedtest Threshold Breached - #{{ $id }}
2+
3+
A new speedtest was completed using {{ $service }} on {{ $isp }} but a threshold was breached.
4+
5+
@foreach ($metrics as $item)
6+
- {{ $item['name'] }} {{ $item['threshold'] }}: {{ $item['value'] }}
7+
@endforeach
8+
- URL: {{ $url }}

0 commit comments

Comments
 (0)