diff --git a/app/Controllers/NotificationGroupController.php b/app/Controllers/NotificationGroupController.php index 067979d..a171174 100644 --- a/app/Controllers/NotificationGroupController.php +++ b/app/Controllers/NotificationGroupController.php @@ -463,7 +463,9 @@ class NotificationGroupController extends Controller 'email' => 'Email', 'telegram' => 'Telegram', 'discord' => 'Discord', - 'slack' => 'Slack' + 'slack' => 'Slack', + 'mattermost' => 'Mattermost', + 'webhook' => 'Webhook' ]; $channelName = $channelNames[$channelType] ?? ucfirst($channelType); @@ -532,6 +534,17 @@ class NotificationGroupController extends Controller } return ['webhook_url' => $webhookUrl]; + case 'mattermost': + $webhookUrl = trim($data['mattermost_webhook_url'] ?? ''); + if (empty($webhookUrl) || !filter_var($webhookUrl, FILTER_VALIDATE_URL)) { + return null; + } + // Validate Mattermost webhook URL format + if (!str_contains($webhookUrl, '/hooks/')) { + return null; + } + return ['webhook_url' => $webhookUrl]; + case 'webhook': $webhookUrl = trim($data['webhook_url'] ?? ''); if (empty($webhookUrl) || !filter_var($webhookUrl, FILTER_VALIDATE_URL)) { diff --git a/app/Services/Channels/MattermostChannel.php b/app/Services/Channels/MattermostChannel.php new file mode 100644 index 0000000..ba66734 --- /dev/null +++ b/app/Services/Channels/MattermostChannel.php @@ -0,0 +1,107 @@ +client = new Client(['timeout' => 10]); + $this->logger = new Logger('mattermost_channel'); + } + + public function send(array $config, string $message, array $data = []): bool + { + if (!isset($config['webhook_url'])) { + return false; + } + + try { + // Mattermost expects a simple text payload or attachments + $payload = [ + 'text' => $message + ]; + + // Add attachments for richer formatting if domain data is available + if (isset($data['domain'])) { + $color = $this->getColorByDaysLeft($data['days_left'] ?? null); + + $payload['attachments'] = [ + [ + 'color' => $color, + 'title' => '🔔 Domain Expiration Alert', + 'text' => $message, + 'fields' => [ + [ + 'short' => true, + 'title' => 'Domain', + 'value' => $data['domain'] + ], + [ + 'short' => true, + 'title' => 'Days Left', + 'value' => $data['days_left'] ?? 'N/A' + ], + [ + 'short' => true, + 'title' => 'Expiration Date', + 'value' => $data['expiration_date'] ?? 'N/A' + ], + [ + 'short' => true, + 'title' => 'Registrar', + 'value' => $data['registrar'] ?? 'N/A' + ] + ], + 'footer' => 'Domain Monitor', + 'ts' => time() + ] + ]; + } + + $response = $this->client->post($config['webhook_url'], [ + 'json' => $payload + ]); + + $ok = $response->getStatusCode() === 200; + if ($ok) { + $this->logger->info('Mattermost message sent', [ + 'status' => $response->getStatusCode() + ]); + } else { + $this->logger->error('Mattermost non-200 status', [ + 'status' => $response->getStatusCode() + ]); + } + return $ok; + } catch (\Exception $e) { + $this->logger->error('Mattermost send failed', [ + 'exception' => $e->getMessage() + ]); + return false; + } + } + + private function getColorByDaysLeft(?int $daysLeft): string + { + if ($daysLeft === null) { + return '#36a64f'; // Green + } + + if ($daysLeft <= 0) { + return '#ff0000'; // Red - expired + } elseif ($daysLeft <= 1) { + return '#ff6600'; // Orange - critical + } elseif ($daysLeft <= 7) { + return '#ffaa00'; // Yellow - warning + } else { + return '#36a64f'; // Green - ok + } + } +} diff --git a/app/Services/NotificationService.php b/app/Services/NotificationService.php index f518224..066e589 100644 --- a/app/Services/NotificationService.php +++ b/app/Services/NotificationService.php @@ -6,6 +6,7 @@ use App\Services\Channels\EmailChannel; use App\Services\Channels\TelegramChannel; use App\Services\Channels\DiscordChannel; use App\Services\Channels\SlackChannel; +use App\Services\Channels\MattermostChannel; use App\Services\Channels\WebhookChannel; class NotificationService @@ -19,6 +20,7 @@ class NotificationService 'telegram' => new TelegramChannel(), 'discord' => new DiscordChannel(), 'slack' => new SlackChannel(), + 'mattermost' => new MattermostChannel(), 'webhook' => new WebhookChannel(), ]; } diff --git a/app/Views/groups/edit.php b/app/Views/groups/edit.php index 3e89c20..f0c7497 100644 --- a/app/Views/groups/edit.php +++ b/app/Views/groups/edit.php @@ -77,9 +77,9 @@ ob_start();
'fa-envelope', 'telegram' => 'fa-telegram', 'discord' => 'fa-discord', 'slack' => 'fa-slack', 'webhook' => 'fa-link']; - $iconClasses = ['email' => 'fas', 'telegram' => 'fab', 'discord' => 'fab', 'slack' => 'fab', 'webhook' => 'fas']; - $colors = ['email' => 'blue', 'telegram' => 'blue', 'discord' => 'indigo', 'slack' => 'teal', 'webhook' => 'purple']; + $icons = ['email' => 'fa-envelope', 'telegram' => 'fa-telegram', 'discord' => 'fa-discord', 'slack' => 'fa-slack', 'mattermost' => 'fa-comments', 'webhook' => 'fa-link']; + $iconClasses = ['email' => 'fas', 'telegram' => 'fab', 'discord' => 'fab', 'slack' => 'fab', 'mattermost' => 'fab', 'webhook' => 'fas']; + $colors = ['email' => 'blue', 'telegram' => 'blue', 'discord' => 'indigo', 'slack' => 'teal', 'mattermost' => 'green', 'webhook' => 'purple']; $icon = $icons[$channel['channel_type']] ?? 'fa-bell'; $iconClass = $iconClasses[$channel['channel_type']] ?? 'fas'; $color = $colors[$channel['channel_type']] ?? 'gray'; @@ -157,6 +157,7 @@ ob_start(); +
@@ -242,6 +243,24 @@ ob_start(); + + +