Skip to content

Commit eb9a3e6

Browse files
authored
Chore: Improve Apprise Logic & UI Text (#2579)
1 parent dc4dd2f commit eb9a3e6

File tree

5 files changed

+118
-34
lines changed

5 files changed

+118
-34
lines changed

app/Actions/Notifications/SendAppriseTestNotification.php

Lines changed: 66 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,17 @@
33
namespace App\Actions\Notifications;
44

55
use App\Notifications\Apprise\TestNotification;
6+
use App\Settings\NotificationSettings;
67
use Filament\Notifications\Notification;
78
use Illuminate\Support\Facades\Notification as FacadesNotification;
89
use Lorisleiva\Actions\Concerns\AsAction;
10+
use Throwable;
911

1012
class SendAppriseTestNotification
1113
{
1214
use AsAction;
1315

14-
public function handle(array $channel_urls)
16+
public function handle(array $channel_urls): void
1517
{
1618
if (! count($channel_urls)) {
1719
Notification::make()
@@ -22,24 +24,77 @@ public function handle(array $channel_urls)
2224
return;
2325
}
2426

25-
foreach ($channel_urls as $row) {
26-
$channelUrl = $row['channel_url'] ?? null;
27-
if (! $channelUrl) {
28-
Notification::make()
29-
->title('Skipping missing channel URL!')
30-
->warning()
31-
->send();
27+
$settings = app(NotificationSettings::class);
28+
$appriseUrl = rtrim($settings->apprise_server_url ?? '', '/');
3229

33-
continue;
30+
if (empty($appriseUrl)) {
31+
Notification::make()
32+
->title('Apprise Server URL is not configured')
33+
->body('Please configure the Apprise Server URL in the settings above.')
34+
->danger()
35+
->send();
36+
37+
return;
38+
}
39+
40+
try {
41+
foreach ($channel_urls as $row) {
42+
$channelUrl = $row['channel_url'] ?? null;
43+
if (! $channelUrl) {
44+
continue;
45+
}
46+
47+
// Use notifyNow() to send synchronously even though notification implements ShouldQueue
48+
// This allows us to catch exceptions and show them in the UI immediately
49+
FacadesNotification::route('apprise_urls', $channelUrl)
50+
->notifyNow(new TestNotification);
3451
}
52+
} catch (Throwable $e) {
53+
$errorMessage = $this->cleanErrorMessage($e);
54+
55+
Notification::make()
56+
->title('Failed to send Apprise test notification')
57+
->body($errorMessage)
58+
->danger()
59+
->send();
3560

36-
FacadesNotification::route('apprise_urls', $channelUrl)
37-
->notify(new TestNotification);
61+
return;
3862
}
3963

4064
Notification::make()
4165
->title('Test Apprise notification sent.')
4266
->success()
4367
->send();
4468
}
69+
70+
/**
71+
* Clean up error message for display in UI.
72+
*/
73+
protected function cleanErrorMessage(Throwable $e): string
74+
{
75+
$message = $e->getMessage();
76+
77+
// Get the full Apprise server URL for error messages
78+
$settings = app(NotificationSettings::class);
79+
$appriseUrl = rtrim($settings->apprise_server_url ?? '', '/');
80+
81+
// Handle connection errors - extract just the important part
82+
if (str_contains($message, 'cURL error')) {
83+
if (str_contains($message, 'Could not resolve host')) {
84+
return "Could not connect to Apprise server at {$appriseUrl}";
85+
}
86+
87+
if (str_contains($message, 'Connection refused')) {
88+
return "Connection refused by Apprise server at {$appriseUrl}";
89+
}
90+
91+
if (str_contains($message, 'Operation timed out')) {
92+
return "Connection to Apprise server at {$appriseUrl} timed out";
93+
}
94+
95+
return "Failed to connect to Apprise server at {$appriseUrl}";
96+
}
97+
98+
return $message;
99+
}
45100
}

app/Filament/Pages/Settings/Notification.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use App\Actions\Notifications\SendTelegramTestNotification;
1515
use App\Actions\Notifications\SendWebhookTestNotification;
1616
use App\Rules\AppriseScheme;
17+
use App\Rules\ContainsString;
1718
use App\Settings\NotificationSettings;
1819
use CodeWithDennis\SimpleAlert\Components\SimpleAlert;
1920
use Filament\Actions\Action;
@@ -233,10 +234,12 @@ public function form(Schema $schema): Schema
233234
->schema([
234235
TextInput::make('apprise_server_url')
235236
->label(__('settings/notifications.apprise_server_url'))
236-
->placeholder('http://localhost:8000')
237+
->placeholder('http://localhost:8000/notify')
238+
->helperText(__('settings/notifications.apprise_server_url_helper'))
237239
->maxLength(2000)
238240
->required()
239241
->url()
242+
->rule(new ContainsString('/notify'))
240243
->columnSpanFull(),
241244
Checkbox::make('apprise_verify_ssl')
242245
->label(__('settings/notifications.apprise_verify_ssl'))

app/Notifications/AppriseChannel.php

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@
33
namespace App\Notifications;
44

55
use App\Settings\NotificationSettings;
6+
use Exception;
67
use Illuminate\Notifications\Notification;
78
use Illuminate\Support\Facades\Http;
89
use Illuminate\Support\Facades\Log;
10+
use Throwable;
911

1012
class AppriseChannel
1113
{
@@ -22,7 +24,7 @@ public function send(object $notifiable, Notification $notification): void
2224
}
2325

2426
$settings = app(NotificationSettings::class);
25-
$appriseUrl = rtrim($settings->apprise_server_url ?? '', '/');
27+
$appriseUrl = $settings->apprise_server_url ?? '';
2628

2729
if (empty($appriseUrl)) {
2830
Log::warning('Apprise notification skipped: No Server URL configured');
@@ -41,7 +43,7 @@ public function send(object $notifiable, Notification $notification): void
4143
$request = $request->withoutVerifying();
4244
}
4345

44-
$response = $request->post("{$appriseUrl}/notify", [
46+
$response = $request->post($appriseUrl, [
4547
'urls' => $message->urls,
4648
'title' => $message->title,
4749
'body' => $message->body,
@@ -50,26 +52,25 @@ public function send(object $notifiable, Notification $notification): void
5052
'tag' => $message->tag ?? null,
5153
]);
5254

53-
if ($response->failed()) {
54-
Log::error('Apprise notification failed', [
55-
'channel' => $message->urls,
56-
'instance' => $appriseUrl,
57-
'status' => $response->status(),
58-
'body' => $response->body(),
59-
]);
60-
} else {
61-
Log::info('Apprise notification sent', [
62-
'channel' => $message->urls,
63-
'instance' => $appriseUrl,
64-
]);
55+
// Only accept 200 OK responses as successful
56+
if ($response->status() !== 200) {
57+
throw new Exception('Apprise returned an error, please check Apprise logs for details');
6558
}
66-
} catch (\Throwable $e) {
67-
Log::error('Apprise notification exception', [
59+
60+
Log::info('Apprise notification sent', [
61+
'channel' => $message->urls,
62+
'instance' => $appriseUrl,
63+
]);
64+
} catch (Throwable $e) {
65+
Log::error('Apprise notification failed', [
6866
'channel' => $message->urls ?? 'unknown',
6967
'instance' => $appriseUrl,
7068
'message' => $e->getMessage(),
7169
'exception' => get_class($e),
7270
]);
71+
72+
// Re-throw the exception so it can be handled by the queue
73+
throw $e;
7374
}
7475
}
7576
}

app/Rules/ContainsString.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
3+
namespace App\Rules;
4+
5+
use Closure;
6+
use Illuminate\Contracts\Validation\ValidationRule;
7+
8+
class ContainsString implements ValidationRule
9+
{
10+
public function __construct(
11+
protected string $needle,
12+
protected bool $caseSensitive = false
13+
) {}
14+
15+
public function validate(string $attribute, mixed $value, Closure $fail): void
16+
{
17+
$haystack = $this->caseSensitive ? $value : strtolower($value);
18+
$needle = $this->caseSensitive ? $this->needle : strtolower($this->needle);
19+
20+
if (! str_contains($haystack, $needle)) {
21+
$fail("The :attribute must contain '{$this->needle}'.");
22+
}
23+
}
24+
}

lang/en/settings/notifications.php

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,19 +19,20 @@
1919
'enable_apprise_notifications' => 'Enable Apprise notifications',
2020
'apprise_server' => 'Apprise Server',
2121
'apprise_server_url' => 'Apprise Server URL',
22+
'apprise_server_url_helper' => 'The URL of your Apprise Server. The URL must end on /notify',
2223
'apprise_verify_ssl' => 'Verify SSL',
23-
'apprise_channels' => 'Apprise Channels',
24-
'apprise_channel_url' => 'Channel URL',
25-
'apprise_hint_description' => 'For more information on setting up Apprise, view the documentation.',
26-
'apprise_channel_url_helper' => 'Provide the service endpoint URL for notifications.',
24+
'apprise_channels' => 'Notification Channels',
25+
'apprise_channel_url' => 'Service URL',
26+
'apprise_hint_description' => 'Apprise allows you to send notifications to 90+ services. You need to run an Apprise server and configure service URLs below.',
27+
'apprise_channel_url_helper' => 'Use Apprise URL format. Examples: discord://WebhookID/Token, slack://TokenA/TokenB/TokenC',
2728
'test_apprise_channel' => 'Test Apprise',
28-
'apprise_channel_url_validation_error' => 'The Apprise channel URL must not start with "http" or "https". Please provide a valid Apprise URL scheme.',
29+
'apprise_channel_url_validation_error' => 'Invalid Apprise URL. Must use Apprise format (e.g., discord://, slack://), not http:// or https://. See the Apprise documentation for more information',
2930

3031
// Webhook
3132
'webhook' => 'Webhook',
3233
'webhooks' => 'Webhooks',
3334
'test_webhook_channel' => 'Test webhook channel',
34-
'webhook_hint_description' => 'These are generic webhooks. For payload examples and implementation details, view the documentation.',
35+
'webhook_hint_description' => 'These are generic webhooks. For payload examples and implementation details, view the documentation. For services like Discord, Ntfy etc please use Apprise.',
3536

3637
// Common notification messages
3738
'notify_on_every_speedtest_run' => 'Notify on every scheduled speedtest run',

0 commit comments

Comments
 (0)