# 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 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 $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 $expiresAt->format('Y-m-d H:i:s'), 'Days Remaining' => $daysRemaining, ], $daysRemaining <= 7 ? 'critical' : 'warning' ); } } ``` ### 3. DomainExpiryAlert Create `app/Notifications/DomainExpiryAlert.php`: ```php $domain, 'Expires On' => $expiresAt->format('Y-m-d'), 'Days Remaining' => $daysRemaining, ], $daysRemaining <= 7 ? 'critical' : 'warning' ); } } ``` ### 4. SitemapAlert Create `app/Notifications/SitemapAlert.php`: ```php $sitemapUrl, 'Error' => $error, ], 'warning' ); } } ``` ### 5. PerformanceAlert Create `app/Notifications/PerformanceAlert.php`: ```php 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 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