Files
ismysiteup.online/MULTI_CHANNEL_NOTIFICATIONS_GUIDE.md
2026-01-24 12:16:10 +02:00

647 lines
16 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Multi-Channel Notifications Implementation Guide
This guide provides a complete implementation for multi-channel monitoring alerts in your Laravel application.
## Overview
The notification system supports the following channels:
- **Email** (built-in Laravel)
- **Database** (built-in Laravel)
- **Slack** (via webhook)
- **Telegram** (via Bot API)
- **Discord** (via webhook)
- **Microsoft Teams** (via webhook)
- **Google Chat** (via webhook)
- **Custom Webhook** (generic HTTP POST)
- **SMS** (via Twilio/Vonage - requires additional setup)
- **Opsgenie** (via API)
- **PagerDuty** (via Events API)
## Installation Complete
The following packages have been installed:
```bash
composer require laravel/slack-notification-channel
composer require laravel-notification-channels/telegram
composer require laravel-notification-channels/discord
```
## Database Schema
Migration created: `2026_01_18_123539_add_notification_channels_to_users_table.php`
Fields added to `users` table:
- `notification_channels` (JSON) - Stores enabled channels per alert type
- `slack_webhook_url` - Slack incoming webhook URL
- `telegram_chat_id` - Telegram bot chat ID
- `discord_webhook_url` - Discord webhook URL
- `teams_webhook_url` - Microsoft Teams webhook URL
- `google_chat_webhook_url` - Google Chat webhook URL
- `webhook_url` - Custom webhook endpoint
- `sms_phone_number` - Phone number for SMS alerts
- `opsgenie_api_key` - Opsgenie API key
- `pagerduty_integration_key` - PagerDuty integration key
## Alert Types
The system supports these alert types:
- `uptime` - Site down/up alerts
- `performance` - Performance threshold alerts
- `ssl` - SSL certificate expiration
- `dns` - DNS record changes
- `broken_links` - Broken link detection
- `domain_expiry` - Domain expiration warnings
- `sitemap` - Sitemap validation issues
- `health_check` - Health endpoint failures
- `tcp_port` - TCP port monitoring
## Base Notification Class
Create `app/Notifications/MonitorAlert.php`:
```php
<?php
namespace App\Notifications;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Messages\SlackMessage;
use Illuminate\Notifications\Notification;
use NotificationChannels\Discord\DiscordChannel;
use NotificationChannels\Discord\DiscordMessage;
use NotificationChannels\Telegram\TelegramChannel;
use NotificationChannels\Telegram\TelegramMessage;
abstract class MonitorAlert extends Notification implements ShouldQueue
{
use Queueable;
public string $alertType;
public string $siteName;
public string $message;
public array $data;
public string $severity; // info, warning, critical
public function __construct(string $alertType, string $siteName, string $message, array $data = [], string $severity = 'warning')
{
$this->alertType = $alertType;
$this->siteName = $siteName;
$this->message = $message;
$this->data = $data;
$this->severity = $severity;
}
/**
* Get the notification's delivery channels.
*/
public function via($notifiable): array
{
$enabledChannels = $notifiable->getNotificationChannelsFor($this->alertType);
$channelMap = [
'email' => 'mail',
'database' => 'database',
'slack' => 'slack',
'telegram' => TelegramChannel::class,
'discord' => DiscordChannel::class,
];
$channels = [];
foreach ($enabledChannels as $channel) {
if (isset($channelMap[$channel])) {
$channels[] = $channelMap[$channel];
}
}
return $channels ?: ['database'];
}
/**
* Get the mail representation of the notification.
*/
public function toMail($notifiable): MailMessage
{
$color = $this->getColorForSeverity();
return (new MailMessage)
->subject("[{$this->getSeverityLabel()}] {$this->siteName} - {$this->getAlertTypeLabel()}")
->greeting("Alert: {$this->siteName}")
->line($this->message)
->when(!empty($this->data), function ($mail) {
foreach ($this->data as $key => $value) {
$mail->line("**{$key}:** {$value}");
}
})
->action('View Dashboard', url('/dashboard'))
->line('Timestamp: ' . now()->format('Y-m-d H:i:s T'));
}
/**
* Get the Slack representation of the notification.
*/
public function toSlack($notifiable): SlackMessage
{
$color = $this->getColorForSeverity();
$message = (new SlackMessage)
->from('Site Monitor', ':robot_face:')
->to('#monitoring')
->attachment(function ($attachment) use ($color) {
$attachment->title($this->siteName, url('/dashboard'))
->content($this->message)
->color($color)
->timestamp(now());
foreach ($this->data as $key => $value) {
$attachment->field($key, $value, true);
}
});
return $message;
}
/**
* Get the Telegram representation of the notification.
*/
public function toTelegram($notifiable): TelegramMessage
{
$emoji = $this->getEmojiForSeverity();
$text = "{$emoji} **{$this->getSeverityLabel()} Alert**\n\n";
$text .= "**Site:** {$this->siteName}\n";
$text .= "**Type:** {$this->getAlertTypeLabel()}\n\n";
$text .= "{$this->message}\n\n";
if (!empty($this->data)) {
$text .= "**Details:**\n";
foreach ($this->data as $key => $value) {
$text .= "• {$key}: {$value}\n";
}
}
$text .= "\n_" . now()->format('Y-m-d H:i:s T') . "_";
return TelegramMessage::create()
->to($notifiable->telegram_chat_id)
->content($text)
->button('View Dashboard', url('/dashboard'));
}
/**
* Get the Discord representation of the notification.
*/
public function toDiscord($notifiable): DiscordMessage
{
$color = $this->getDiscordColorForSeverity();
return DiscordMessage::create()
->embed(function ($embed) use ($color) {
$embed->title("{$this->siteName} - {$this->getAlertTypeLabel()}")
->description($this->message)
->color($color)
->timestamp(now());
foreach ($this->data as $key => $value) {
$embed->field($key, $value, true);
}
$embed->footer('Site Monitoring Alert');
});
}
/**
* Get the array representation of the notification (database).
*/
public function toArray($notifiable): array
{
return [
'alert_type' => $this->alertType,
'site_name' => $this->siteName,
'message' => $this->message,
'severity' => $this->severity,
'data' => $this->data,
'timestamp' => now()->toDateTimeString(),
];
}
/**
* Get color based on severity
*/
protected function getColorForSeverity(): string
{
return match ($this->severity) {
'critical' => 'danger',
'warning' => 'warning',
default => 'info',
};
}
/**
* Get Discord color based on severity
*/
protected function getDiscordColorForSeverity(): int
{
return match ($this->severity) {
'critical' => 0xFF0000, // Red
'warning' => 0xFFA500, // Orange
default => 0x0099FF, // Blue
};
}
/**
* Get emoji based on severity
*/
protected function getEmojiForSeverity(): string
{
return match ($this->severity) {
'critical' => '🚨',
'warning' => '⚠️',
default => 'ℹ️',
};
}
/**
* Get severity label
*/
protected function getSeverityLabel(): string
{
return strtoupper($this->severity);
}
/**
* Get alert type label
*/
protected function getAlertTypeLabel(): string
{
return str_replace('_', ' ', ucwords($this->alertType));
}
}
```
## Specific Notification Classes
### 1. UptimeAlert
Create `app/Notifications/UptimeAlert.php`:
```php
<?php
namespace App\Notifications;
class UptimeAlert extends MonitorAlert
{
public function __construct(string $siteName, bool $isDown, int $statusCode, string $url, float $responseTime = null)
{
$message = $isDown
? "Your site is DOWN (HTTP {$statusCode})"
: "Your site is back ONLINE";
$data = [
'Status Code' => $statusCode,
'URL' => $url,
];
if ($responseTime) {
$data['Response Time'] = round($responseTime, 2) . 'ms';
}
parent::__construct(
'uptime',
$siteName,
$message,
$data,
$isDown ? 'critical' : 'info'
);
}
}
```
### 2. SslExpiryAlert
Create `app/Notifications/SslExpiryAlert.php`:
```php
<?php
namespace App\Notifications;
use Carbon\Carbon;
class SslExpiryAlert extends MonitorAlert
{
public function __construct(string $siteName, Carbon $expiresAt, int $daysRemaining)
{
$message = $daysRemaining <= 0
? "SSL certificate has EXPIRED"
: "SSL certificate expires in {$daysRemaining} days";
parent::__construct(
'ssl',
$siteName,
$message,
[
'Expires On' => $expiresAt->format('Y-m-d H:i:s'),
'Days Remaining' => $daysRemaining,
],
$daysRemaining <= 7 ? 'critical' : 'warning'
);
}
}
```
### 3. DomainExpiryAlert
Create `app/Notifications/DomainExpiryAlert.php`:
```php
<?php
namespace App\Notifications;
use Carbon\Carbon;
class DomainExpiryAlert extends MonitorAlert
{
public function __construct(string $siteName, string $domain, Carbon $expiresAt, int $daysRemaining)
{
$message = $daysRemaining <= 0
? "Domain '{$domain}' has EXPIRED"
: "Domain '{$domain}' expires in {$daysRemaining} days";
parent::__construct(
'domain_expiry',
$siteName,
$message,
[
'Domain' => $domain,
'Expires On' => $expiresAt->format('Y-m-d'),
'Days Remaining' => $daysRemaining,
],
$daysRemaining <= 7 ? 'critical' : 'warning'
);
}
}
```
### 4. SitemapAlert
Create `app/Notifications/SitemapAlert.php`:
```php
<?php
namespace App\Notifications;
class SitemapAlert extends MonitorAlert
{
public function __construct(string $siteName, string $sitemapUrl, string $error)
{
parent::__construct(
'sitemap',
$siteName,
'Sitemap validation failed',
[
'Sitemap URL' => $sitemapUrl,
'Error' => $error,
],
'warning'
);
}
}
```
### 5. PerformanceAlert
Create `app/Notifications/PerformanceAlert.php`:
```php
<?php
namespace App\Notifications;
class PerformanceAlert extends MonitorAlert
{
public function __construct(string $siteName, float $responseTime, float $threshold)
{
parent::__construct(
'performance',
$siteName,
"Response time exceeded threshold",
[
'Response Time' => round($responseTime, 2) . 'ms',
'Threshold' => round($threshold, 2) . 'ms',
'Difference' => '+' . round($responseTime - $threshold, 2) . 'ms',
],
'warning'
);
}
}
```
## Integration with Existing Jobs
Update your monitoring jobs to send notifications. Example for `CheckSitemap`:
```php
// In app/Jobs/CheckSitemap.php
use App\Notifications\SitemapAlert;
protected function sendAlert(string $message): void
{
// Send notification using the new system
$this->sitemap->site->user->notify(
new SitemapAlert(
$this->sitemap->site->name,
$this->sitemap->url,
$this->sitemap->last_error
)
);
// Keep existing Notification model creation for backwards compatibility
Notification::create([
'user_id' => $this->sitemap->site->user_id,
'type' => 'sitemap_invalid',
'message' => "{$message}: {$this->sitemap->url}",
'data' => json_encode([
'sitemap_id' => $this->sitemap->id,
'site_id' => $this->sitemap->site_id,
'url' => $this->sitemap->url,
'error' => $this->sitemap->last_error,
]),
'read_at' => null,
]);
}
```
## Configuration Files
### Services Configuration
Add to `config/services.php`:
```php
'telegram-bot-api' => [
'token' => env('TELEGRAM_BOT_TOKEN'),
],
'twilio' => [
'sid' => env('TWILIO_SID'),
'token' => env('TWILIO_TOKEN'),
'from' => env('TWILIO_FROM'),
],
'opsgenie' => [
'api_key' => env('OPSGENIE_API_KEY'),
'api_url' => 'https://api.opsgenie.com',
],
'pagerduty' => [
'api_url' => 'https://events.pagerduty.com/v2/enqueue',
],
```
### Environment Variables
Add to `.env`:
```env
# Telegram Bot
TELEGRAM_BOT_TOKEN=your_bot_token_here
# Twilio SMS
TWILIO_SID=your_account_sid
TWILIO_TOKEN=your_auth_token
TWILIO_FROM=+1234567890
# Opsgenie
OPSGENIE_API_KEY=your_api_key
# Global defaults (optional)
DEFAULT_SLACK_CHANNEL=#monitoring
DEFAULT_NOTIFICATION_QUEUE=notifications
```
## Notification Settings Controller
The controller and views for managing notification settings will be created separately to allow users to:
- Configure which channels to use for each alert type
- Enter webhook URLs and API keys
- Test notification delivery
- View notification history
## Channel Setup Guides
### Slack
1. Create an Incoming Webhook in your Slack workspace
2. Copy the webhook URL
3. Paste in notification settings
### Telegram
1. Create a bot via @BotFather
2. Get your bot token
3. Start a chat with your bot
4. Get your chat ID (use @userinfobot)
5. Enter chat ID in settings
### Discord
1. Edit channel settings
2. Go to Integrations > Webhooks
3. Create webhook
4. Copy webhook URL
### Microsoft Teams
1. Add "Incoming Webhook" connector to channel
2. Configure and create
3. Copy webhook URL
### Google Chat
1. Create a webhook in Google Chat space
2. Copy webhook URL
## Testing Notifications
Create a test command:
```bash
php artisan make:command TestNotifications
```
```php
<?php
namespace App\Console\Commands;
use App\Notifications\UptimeAlert;
use App\Models\User;
use Illuminate\Console\Command;
class TestNotifications extends Command
{
protected $signature = 'notifications:test {user}';
protected $description = 'Test notification delivery for a user';
public function handle()
{
$user = User::findOrFail($this->argument('user'));
$user->notify(new UptimeAlert(
'Test Site',
false, // Not down
200,
'https://example.com',
150.5
));
$this->info('Test notification sent!');
}
}
```
## Best Practices
1. **Queue Notifications**: All notifications implement `ShouldQueue`
2. **Rate Limiting**: Implement rate limiting to prevent notification spam
3. **Retry Logic**: Configure queue retry attempts
4. **Monitoring**: Log notification failures
5. **Testing**: Test each channel before production use
6. **Security**: Encrypt sensitive webhook URLs and API keys
7. **User Preferences**: Respect user notification preferences
8. **Batching**: Consider batching similar alerts
## Next Steps
1. Run migration: `php artisan migrate`
2. Create notification settings UI
3. Integrate with existing monitoring jobs
4. Test each notification channel
5. Configure queue worker for notifications
6. Set up monitoring for the notification system itself
## Troubleshooting
### Notifications not sending
- Check queue worker is running
- Verify webhook URLs are correct
- Check logs: `storage/logs/laravel.log`
- Test with `notifications:test` command
### Channel-specific issues
- **Slack**: Verify webhook URL format
- **Telegram**: Ensure bot token is valid and chat ID is correct
- **Discord**: Check webhook hasn't been deleted
- **Email**: Verify mail configuration
## Future Enhancements
- Notification grouping (digest emails)
- Quiet hours configuration
- Escalation policies
- Notification templates
- Webhook retry logic
- Notification analytics
- Mobile push notifications
- In-app notification center