Skip to content

Commit 87d469e

Browse files
committed
Add Support for WeCom Notifications via Webhook
1 parent 9f5fe70 commit 87d469e

File tree

8 files changed

+332
-0
lines changed

8 files changed

+332
-0
lines changed
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
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 SendWeComTestNotification
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 wecom urls!')
18+
->warning()
19+
->send();
20+
21+
return;
22+
}
23+
24+
$payload = [
25+
'msgtype' => 'text',
26+
'text' => [
27+
'content' => '👋 Testing the Webhook notification channel on Speedtest Tracker.',
28+
],
29+
];
30+
31+
foreach ($webhooks as $webhook) {
32+
WebhookCall::create()
33+
->url($webhook['url'])
34+
->payload($payload)
35+
->doNotSign()
36+
->dispatch();
37+
}
38+
39+
Notification::make()
40+
->title('Test wecom notification sent.')
41+
->success()
42+
->send();
43+
}
44+
}

app/Filament/Pages/Settings/NotificationPage.php

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
use App\Actions\Notifications\SendSlackTestNotification;
1414
use App\Actions\Notifications\SendTelegramTestNotification;
1515
use App\Actions\Notifications\SendWebhookTestNotification;
16+
use App\Actions\Notifications\SendWeComTestNotification;
1617
use App\Settings\NotificationSettings;
1718
use Filament\Forms;
1819
use Filament\Forms\Form;
@@ -562,6 +563,51 @@ public function form(Form $form): Form
562563
'default' => 1,
563564
'md' => 2,
564565
]),
566+
567+
Forms\Components\Section::make('WeCom')
568+
->schema([
569+
Forms\Components\Toggle::make('wecom_enabled')
570+
->label('Enable wecom notifications')
571+
->reactive()
572+
->columnSpanFull(),
573+
Forms\Components\Grid::make([
574+
'default' => 1,
575+
])
576+
->hidden(fn (Forms\Get $get) => $get('wecom_enabled') !== true)
577+
->schema([
578+
Forms\Components\Fieldset::make('Triggers')
579+
->schema([
580+
Forms\Components\Toggle::make('wecom_on_speedtest_run')
581+
->label('Notify on every speedtest run')
582+
->columnSpan(2),
583+
Forms\Components\Toggle::make('wecom_on_threshold_failure')
584+
->label('Notify on threshold failures')
585+
->columnSpan(2),
586+
]),
587+
Forms\Components\Repeater::make('wecom_webhooks')
588+
->label('Webhooks')
589+
->schema([
590+
Forms\Components\TextInput::make('url')
591+
->placeholder('https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=xxxxxxxx')
592+
->maxLength(2000)
593+
->required()
594+
->url(),
595+
])
596+
->columnSpanFull(),
597+
Forms\Components\Actions::make([
598+
Forms\Components\Actions\Action::make('test wecom')
599+
->label('Test wecom channel')
600+
->action(fn (Forms\Get $get) => SendWeComTestNotification::run(webhooks: $get('wecom_webhooks')))
601+
->hidden(fn (Forms\Get $get) => ! count($get('wecom_webhooks'))),
602+
]),
603+
]),
604+
])
605+
->compact()
606+
->columns([
607+
'default' => 1,
608+
'md' => 2,
609+
]),
610+
565611
])
566612
->columnSpan([
567613
'md' => 2,
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\WeCom;
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->wecom_enabled) {
22+
return;
23+
}
24+
25+
if (! $notificationSettings->wecom_on_speedtest_run) {
26+
return;
27+
}
28+
29+
if (! count($notificationSettings->wecom_webhooks)) {
30+
Log::warning('WeCom urls not found, check wecom notification channel settings.');
31+
32+
return;
33+
}
34+
35+
$payload = [
36+
'msgtype' => 'markdown',
37+
'markdown' => [
38+
'content' => view('wecom.speedtest-completed', [
39+
'id' => $event->result->id,
40+
'service' => Str::title($event->result->service->getLabel()),
41+
'serverName' => $event->result->server_name,
42+
'serverId' => $event->result->server_id,
43+
'isp' => $event->result->isp,
44+
'ping' => round($event->result->ping).' ms',
45+
'download' => Number::toBitRate(bits: $event->result->download_bits, precision: 2),
46+
'upload' => Number::toBitRate(bits: $event->result->upload_bits, precision: 2),
47+
'packetLoss' => is_numeric($event->result->packet_loss) ? round($event->result->packet_loss, 2) : 'n/a',
48+
'speedtest_url' => $event->result->result_url,
49+
'url' => url('/admin/results'),
50+
])->render(),
51+
],
52+
];
53+
54+
foreach ($notificationSettings->wecom_webhooks as $webhook) {
55+
WebhookCall::create()
56+
->url($webhook['url'])
57+
->payload($payload)
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\WeCom;
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->wecom_enabled) {
23+
return;
24+
}
25+
26+
if (! $notificationSettings->wecom_on_threshold_failure) {
27+
return;
28+
}
29+
30+
if (! count($notificationSettings->wecom_webhooks)) {
31+
Log::warning('WeCom urls not found, check wecom 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 wecom thresholds not found, won\'t send notification.');
60+
61+
return;
62+
}
63+
64+
$payload = [
65+
'msgtype' => 'markdown',
66+
'markdown' => [
67+
'content' => view('wecom.speedtest-threshold', [
68+
'id' => $event->result->id,
69+
'service' => Str::title($event->result->service->getLabel()),
70+
'serverName' => $event->result->server_name,
71+
'serverId' => $event->result->server_id,
72+
'isp' => $event->result->isp,
73+
'metrics' => $failed,
74+
'speedtest_url' => $event->result->result_url,
75+
'url' => url('/admin/results'),
76+
])->render(),
77+
],
78+
];
79+
80+
foreach ($notificationSettings->wecom_webhooks as $webhook) {
81+
WebhookCall::create()
82+
->url($webhook['url'])
83+
->payload($payload)
84+
->doNotSign()
85+
->dispatch();
86+
}
87+
}
88+
89+
/**
90+
* Build webhook 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 webhook 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 webhook 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
@@ -94,6 +94,14 @@ class NotificationSettings extends Settings
9494

9595
public ?array $dingtalk_webhooks;
9696

97+
public bool $wecom_enabled;
98+
99+
public bool $wecom_on_speedtest_run;
100+
101+
public bool $wecom_on_threshold_failure;
102+
103+
public ?array $wecom_webhooks;
104+
97105
public static function group(): string
98106
{
99107
return 'notification';
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.wecom_enabled', false);
10+
$this->migrator->add('notification.wecom_on_speedtest_run', false);
11+
$this->migrator->add('notification.wecom_on_threshold_failure', false);
12+
$this->migrator->add('notification.wecom_webhooks', null);
13+
}
14+
};
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
*Speedtest Completed - #{{ $id }}*
2+
3+
A new speedtest on *{{ config('app.name') }}* 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+
- **Ookla Speedtest:** {{ $speedtest_url }}
13+
- **URL:** {{ $url }}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
**Speedtest Threshold Breached - #{{ $id }}**
2+
3+
A new speedtest on **{{ config('app.name') }}** 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+
- **Ookla Speedtest:** {{ $speedtest_url }}
9+
- **URL:** {{ $url }}

0 commit comments

Comments
 (0)