exec( "CREATE TABLE IF NOT EXISTS tbl_scwebhooks ( cl_scwebhook_id INT(11) NOT NULL AUTO_INCREMENT, cl_scwebhook_name VARCHAR(255) NOT NULL, cl_scwebhook_url TEXT NOT NULL, cl_scwebhook_is_forum TINYINT(1) NOT NULL DEFAULT 0, PRIMARY KEY (cl_scwebhook_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci" ); $db->exec( "CREATE TABLE IF NOT EXISTS tbl_scbanners ( cl_scbanner_id INT(11) NOT NULL AUTO_INCREMENT, cl_scbanner_name VARCHAR(255) NOT NULL, cl_scbanner_url TEXT NOT NULL, cl_scbanner_border_color VARCHAR(20) NOT NULL DEFAULT '#ffae00', PRIMARY KEY (cl_scbanner_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci" ); $columns_stmt = $db->query("SHOW COLUMNS FROM tbl_scbanners LIKE 'cl_scbanner_border_color'"); $has_border_color = (bool) $columns_stmt->fetch(); if (!$has_border_color) { $db->exec("ALTER TABLE tbl_scbanners ADD COLUMN cl_scbanner_border_color VARCHAR(20) NOT NULL DEFAULT '#ffae00' AFTER cl_scbanner_url"); } $db->exec( "CREATE TABLE IF NOT EXISTS tbl_scnotifications ( cl_scnotification_id INT(11) NOT NULL AUTO_INCREMENT, cl_scnotification_webhook_id INT(11) NOT NULL, cl_scnotification_banner_id INT(11) DEFAULT NULL, cl_scnotification_title VARCHAR(255) NOT NULL DEFAULT '', cl_scnotification_message TEXT NOT NULL, cl_scnotification_payload LONGTEXT NOT NULL, cl_scnotification_response LONGTEXT DEFAULT NULL, cl_scnotification_success TINYINT(1) NOT NULL DEFAULT 0, cl_scnotification_created_by VARCHAR(190) NOT NULL, cl_scnotification_created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (cl_scnotification_id), KEY idx_scnotification_webhook (cl_scnotification_webhook_id), KEY idx_scnotification_banner (cl_scnotification_banner_id), CONSTRAINT fk_scnotification_webhook FOREIGN KEY (cl_scnotification_webhook_id) REFERENCES tbl_scwebhooks (cl_scwebhook_id) ON UPDATE CASCADE ON DELETE RESTRICT, CONSTRAINT fk_scnotification_banner FOREIGN KEY (cl_scnotification_banner_id) REFERENCES tbl_scbanners (cl_scbanner_id) ON UPDATE CASCADE ON DELETE SET NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci" ); $stmt_existing_red = $db->prepare('SELECT cl_scbanner_id FROM tbl_scbanners WHERE cl_scbanner_name = :name LIMIT 1'); $stmt_existing_red->execute(['name' => 'Alerte Rouge']); $existing_red_banner = $stmt_existing_red->fetch(PDO::FETCH_ASSOC); if ($existing_red_banner) { $stmt_update_red = $db->prepare( 'UPDATE tbl_scbanners SET cl_scbanner_border_color = CASE WHEN cl_scbanner_border_color IS NULL OR cl_scbanner_border_color = "" OR cl_scbanner_border_color = "#ffae00" THEN :border_color ELSE cl_scbanner_border_color END WHERE cl_scbanner_id = :id' ); $stmt_update_red->execute([ 'border_color' => '#ff3b30', 'id' => $existing_red_banner['cl_scbanner_id'], ]); } $bootstrapped = true; } function scdiscord_mask_webhook_url(string $url): string { $trimmed = trim($url); if ($trimmed === '') { return ''; } $length = strlen($trimmed); if ($length <= 24) { return str_repeat('•', max(8, $length)); } return substr($trimmed, 0, 32) . str_repeat('•', 18) . substr($trimmed, -10); } function scdiscord_normalize_hex_color(string $color): string { $candidate = strtoupper(trim($color)); if ($candidate === '') { return '#FFAE00'; } if ($candidate[0] !== '#') { $candidate = '#' . $candidate; } if (!preg_match('/^#[0-9A-F]{6}$/', $candidate)) { return '#FFAE00'; } return $candidate; } function scdiscord_hex_to_decimal(string $color): int { return hexdec(ltrim(scdiscord_normalize_hex_color($color), '#')); } function scdiscord_build_mentions(bool $notify_here, bool $notify_everyone): array { $parts = []; if ($notify_here) { $parts[] = '@here'; } if ($notify_everyone) { $parts[] = '@everyone'; } return $parts; } function scdiscord_build_thread_name(string $title, string $location, string $start_date): string { $parts = []; if ($title !== '') { $parts[] = $title; } if ($location !== '') { $parts[] = $location; } if ($start_date !== '') { $parts[] = $start_date; } $thread_name = trim(implode(' • ', $parts)); if ($thread_name === '') { $thread_name = 'Notification Discord'; } return mb_substr($thread_name, 0, 100); } function scdiscord_post_webhook(string $webhook_url, array $payload): array { $target_url = $webhook_url; $separator = (str_contains($target_url, '?')) ? '&' : '?'; $target_url .= $separator . 'wait=true'; $json_payload = json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); if ($json_payload === false) { return [ 'success' => false, 'http_code' => 0, 'response' => 'Erreur d\'encodage JSON.', ]; } if (function_exists('curl_init')) { $ch = curl_init($target_url); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_POSTFIELDS => $json_payload, CURLOPT_HTTPHEADER => [ 'Content-Type: application/json', 'Content-Length: ' . strlen($json_payload), ], CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 20, ]); $response = curl_exec($ch); $http_code = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE); $curl_error = curl_error($ch); curl_close($ch); if ($response === false) { return [ 'success' => false, 'http_code' => $http_code, 'response' => $curl_error !== '' ? $curl_error : 'Erreur CURL inconnue.', ]; } return [ 'success' => $http_code >= 200 && $http_code < 300, 'http_code' => $http_code, 'response' => $response, ]; } $context = stream_context_create([ 'http' => [ 'method' => 'POST', 'header' => "Content-Type: application/json\r\n", 'content' => $json_payload, 'timeout' => 20, 'ignore_errors' => true, ], ]); $response = @file_get_contents($target_url, false, $context); $http_code = 0; if (isset($http_response_header) && is_array($http_response_header)) { foreach ($http_response_header as $header_line) { if (preg_match('#^HTTP/\S+\s+(\d{3})#', $header_line, $matches)) { $http_code = (int) $matches[1]; break; } } } return [ 'success' => $response !== false && $http_code >= 200 && $http_code < 300, 'http_code' => $http_code, 'response' => $response === false ? 'Erreur lors de la requête HTTP.' : $response, ]; }