load_library_from_json(); } /** * Charge la bibliothèque de bots depuis le fichier JSON */ private function load_library_from_json() { $json_path = ALMAWEB_PLUGIN_DIR . 'data/bot-library.json'; // Vérifier que le fichier existe if (!file_exists($json_path)) { // Fallback : arrays vides return; } // Charger et décoder JSON $json_content = file_get_contents($json_path); $library = json_decode($json_content, true); // Validation JSON if (json_last_error() !== JSON_ERROR_NONE) { return; } // Vérifier la structure du JSON if (!isset($library['bots'])) { return; } // Stocker la version $this->library_version = $library['version'] ?? '1.0.0'; // Convertir format JSON → format array interne if (isset($library['bots']['ai_bots'])) { foreach ($library['bots']['ai_bots'] as $bot) { if (isset($bot['pattern'], $bot['name'], $bot['family'])) { $this->ai_bots[$bot['pattern']] = [ 'name' => $bot['name'], 'family' => $bot['family'], 'intention' => $bot['intention'] ?? null ]; } } } if (isset($library['bots']['scrapers'])) { foreach ($library['bots']['scrapers'] as $bot) { if (isset($bot['pattern'], $bot['name'], $bot['family'])) { $this->scrapers[$bot['pattern']] = [ 'name' => $bot['name'], 'family' => $bot['family'], 'intention' => $bot['intention'] ?? 'scraping' ]; } } } if (isset($library['bots']['legitimate_bots'])) { foreach ($library['bots']['legitimate_bots'] as $bot) { if (isset($bot['pattern'], $bot['name'], $bot['family'])) { $this->legitimate_bots[$bot['pattern']] = [ 'name' => $bot['name'], 'family' => $bot['family'], 'intention' => $bot['intention'] ?? 'search' ]; } } } } /** * Retourne la version de la bibliothèque chargée */ public function get_library_version() { return $this->library_version; } /** * Analyser la requête actuelle */ public function analyze_request() { $user_agent = $this->get_user_agent(); $ip_address = $this->get_ip_address(); $bot_info = $this->detect_bot($user_agent, $ip_address); return array( 'user_agent' => $user_agent, 'ip_address' => $ip_address, 'bot_name' => $bot_info['name'], 'bot_type' => $bot_info['type'], 'bot_family' => $bot_info['family'], 'bot_intention' => $bot_info['intention'] ?? null, 'is_ai_bot' => $bot_info['is_ai'], 'is_bad_bot' => $bot_info['is_bad'], 'is_stealth' => $bot_info['is_stealth'] ?? false, 'detection_method' => $bot_info['detection_method'] ?? 'user_agent', 'request_url' => $this->get_current_url(), 'referer' => isset($_SERVER['HTTP_REFERER']) ? esc_url_raw(wp_unslash($_SERVER['HTTP_REFERER'])) : null, 'http_accept' => isset($_SERVER['HTTP_ACCEPT']) ? sanitize_text_field(wp_unslash($_SERVER['HTTP_ACCEPT'])) : null, 'http_accept_language' => isset($_SERVER['HTTP_ACCEPT_LANGUAGE']) ? sanitize_text_field(wp_unslash($_SERVER['HTTP_ACCEPT_LANGUAGE'])) : null, 'page_visited' => $this->get_clean_page_path(), 'action_type' => $this->determine_action_type($user_agent), ); } /** * Détecter le type de bot */ private function detect_bot($user_agent, $ip_address) { $result = array( 'name' => null, 'type' => 'unknown', 'family' => null, 'intention' => null, 'is_ai' => false, 'is_bad' => false, 'is_stealth' => false, 'detection_method' => 'user_agent', ); if (empty($user_agent)) { $result['type'] = 'no_user_agent'; $result['is_bad'] = true; return $result; } // IMPORTANT: Vérifier les bots légitimes EN PREMIER foreach ($this->legitimate_bots as $pattern => $bot_data) { if (stripos($user_agent, $pattern) !== false) { $result['name'] = $bot_data['name']; $result['family'] = $bot_data['family']; $result['intention'] = $bot_data['intention'] ?? 'search'; $result['type'] = 'search_engine'; $result['is_ai'] = true; // Mark all bots as AI for dashboard tracking $result['detection_method'] = 'user_agent'; return $result; } } // Vérifier les scrapers foreach ($this->scrapers as $pattern => $bot_data) { if (stripos($user_agent, $pattern) !== false) { $result['name'] = $bot_data['name']; $result['family'] = $bot_data['family']; $result['intention'] = $bot_data['intention'] ?? 'scraping'; $result['type'] = 'scraper'; $result['is_ai'] = true; // Mark all bots as AI for dashboard tracking $result['is_bad'] = false; $result['detection_method'] = 'user_agent'; return $result; } } // Vérifier les bots IA via User-Agent foreach ($this->ai_bots as $pattern => $bot_data) { if (stripos($user_agent, $pattern) !== false) { $result['name'] = $bot_data['name']; $result['family'] = $bot_data['family']; $result['intention'] = $bot_data['intention']; $result['type'] = 'ai_bot'; $result['is_ai'] = true; $result['detection_method'] = 'user_agent'; return $result; } } // Détection générique de bot if ($this->is_generic_bot($user_agent)) { $result['name'] = $this->extract_bot_name($user_agent); $result['type'] = 'generic_bot'; $result['family'] = 'Generic'; return $result; } // Vérifier les bots invisibles via IP (ChatGPT Atlas, etc.) $ip_checker = new Almaweb_IP_Checker(); $stealth_check = $ip_checker->detect_stealth_bot($user_agent, $ip_address); if ($stealth_check && $stealth_check['is_stealth']) { $result['name'] = $stealth_check['suspected_bot']; $result['type'] = 'stealth_ai_bot'; $result['family'] = $stealth_check['family'] ?? 'Unknown'; $result['is_ai'] = true; $result['is_stealth'] = true; $result['detection_method'] = 'ip_range'; return $result; } // Visiteur humain probable $result['type'] = 'human'; $result['family'] = 'Human'; return $result; } /** * Vérifier si c'est un bot générique */ private function is_generic_bot($user_agent) { $bot_patterns = array( 'bot', 'crawl', 'spider', 'scraper', 'curl', 'wget', 'python', 'java', 'http', 'fetch', 'scan', 'check', 'monitor', 'test', 'index', 'search', 'archive', 'slurp', 'validator', 'checker', 'analyzer', 'grabber', 'extractor' ); foreach ($bot_patterns as $pattern) { if (stripos($user_agent, $pattern) !== false) { return true; } } return false; } /** * Extraire le nom du bot depuis le User-Agent */ private function extract_bot_name($user_agent) { // Essayer d'extraire le nom entre parenthèses ou avant un slash // Pattern 1: NomBot/version if (preg_match('/([A-Za-z0-9\-_]+(?:bot|Bot|BOT|spider|Spider|crawler|Crawler))[\/\s]/i', $user_agent, $matches)) { return $matches[1]; } // Pattern 2: compatible; NomBot/version if (preg_match('/compatible;\s*([A-Za-z0-9\-_]+)/i', $user_agent, $matches)) { return $matches[1]; } // Pattern 3: Mozilla/5.0 (compatible; NomBot; ...) if (preg_match('/\(compatible;\s*([^;)]+)/i', $user_agent, $matches)) { return trim($matches[1]); } // Pattern 4: Premiers mots avant slash ou espace if (preg_match('/^([A-Za-z0-9\-_]+)[\s\/]/', $user_agent, $matches)) { return $matches[1]; } return 'Unknown Bot'; } /** * Déterminer le type d'action */ private function determine_action_type($user_agent) { $request_uri = isset($_SERVER['REQUEST_URI']) ? sanitize_text_field(wp_unslash($_SERVER['REQUEST_URI'])) : ''; $request_method = isset($_SERVER['REQUEST_METHOD']) ? sanitize_text_field(wp_unslash($_SERVER['REQUEST_METHOD'])) : 'GET'; if (strpos($request_uri, '/wp-json/') !== false) { return 'api_access'; } if (strpos($request_uri, '/wp-admin') !== false) { return 'admin_attempt'; } if (strpos($request_uri, '/wp-login') !== false) { return 'login_attempt'; } if (strpos($request_uri, 'wp-content') !== false) { return 'resource_access'; } if ($request_method === 'POST') { return 'form_submission'; } return 'page_visit'; } /** * Obtenir le User Agent */ private function get_user_agent() { return isset($_SERVER['HTTP_USER_AGENT']) ? sanitize_text_field(wp_unslash($_SERVER['HTTP_USER_AGENT'])) : 'Unknown'; } /** * Obtenir l'adresse IP */ private function get_ip_address() { $ip_keys = array( 'HTTP_CLIENT_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_X_FORWARDED', 'HTTP_X_CLUSTER_CLIENT_IP', 'HTTP_FORWARDED_FOR', 'HTTP_FORWARDED', 'REMOTE_ADDR' ); foreach ($ip_keys as $key) { if (array_key_exists($key, $_SERVER)) { $ip_list = explode(',', sanitize_text_field(wp_unslash($_SERVER[$key]))); foreach ($ip_list as $ip) { $ip = trim($ip); if (filter_var($ip, FILTER_VALIDATE_IP)) { return $ip; } } } } return 'Unknown'; } /** * Obtenir l'URL actuelle */ private function get_current_url() { $protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? "https://" : "http://"; $host = isset($_SERVER['HTTP_HOST']) ? sanitize_text_field(wp_unslash($_SERVER['HTTP_HOST'])) : ''; $uri = isset($_SERVER['REQUEST_URI']) ? sanitize_text_field(wp_unslash($_SERVER['REQUEST_URI'])) : ''; return $protocol . $host . $uri; } /** * Obtenir le chemin de la page nettoyé, sans les paramètres UTM. */ private function get_clean_page_path() { if (empty($_SERVER['REQUEST_URI'])) { return '/'; } $request_uri = sanitize_text_field(wp_unslash($_SERVER['REQUEST_URI'])); $parsed_url = wp_parse_url($request_uri); $path = $parsed_url['path'] ?? '/'; if (empty($parsed_url['query'])) { return $path; } parse_str($parsed_url['query'], $query_params); foreach ($query_params as $key => $value) { if (strpos($key, 'utm_') === 0) { unset($query_params[$key]); } } if (empty($query_params)) { return $path; } return $path . '?' . http_build_query($query_params); } /** * Obtenir la liste des bots IA */ public function get_ai_bots_list() { return $this->ai_bots; } /** * Obtenir la liste des scrapers */ public function get_scrapers_list() { return $this->scrapers; } /** * Obtenir la liste des bots légitimes */ public function get_legitimate_bots_list() { return $this->legitimate_bots; } }