get_user_agent(); $remote_addr = $this->get_remote_addr(); // Check user agent for known bots if ($this->is_bot_user_agent($user_agent)) { return true; } // Check IP address against known bot ranges if ($this->is_bot_ip($remote_addr)) { return true; } // Check for suspicious patterns if ($this->has_suspicious_patterns($user_agent, $remote_addr)) { return true; } return false; } /** * Gets the sanitized user agent string. * * @return string */ private function get_user_agent() { return isset($_SERVER['HTTP_USER_AGENT']) ? sanitize_text_field(wp_unslash($_SERVER['HTTP_USER_AGENT'])) : ''; } /** * Gets the sanitized remote address. * * @return string */ private function get_remote_addr() { return isset($_SERVER['REMOTE_ADDR']) ? sanitize_text_field(wp_unslash($_SERVER['REMOTE_ADDR'])) : ''; } /** * Checks if the user agent matches known bot patterns. * * @param string $user_agent * @return bool */ private function is_bot_user_agent($user_agent) { if (empty($user_agent)) { return true; // Empty user agent is suspicious } foreach ($this->known_bots as $bot) { if (stripos($user_agent, $bot) !== false) { return true; } } // Check for common bot patterns $bot_patterns = [ '/bot/i', '/crawler/i', '/spider/i', '/scraper/i', '/harvester/i', '/collector/i', '/grabber/i', '/ai/i', '/chatgpt/i', '/claude/i', '/anthropic/i', '/openai/i', '/gpt/i', '/llm/i', '/language-model/i' ]; foreach ($bot_patterns as $pattern) { if (preg_match($pattern, $user_agent)) { return true; } } return false; } /** * Checks if the IP address is in known bot ranges. * * @param string $ip * @return bool */ private function is_bot_ip($ip) { if (empty($ip) || !filter_var($ip, FILTER_VALIDATE_IP)) { return false; } foreach ($this->bot_ip_ranges as $range) { if ($this->ip_in_range($ip, $range)) { return true; } } return false; } /** * Checks if an IP address is within a CIDR range. * * @param string $ip * @param string $cidr * @return bool */ private function ip_in_range($ip, $cidr) { list($subnet, $bits) = explode('/', $cidr); $ip = ip2long($ip); $subnet = ip2long($subnet); $mask = -1 << (32 - $bits); $subnet &= $mask; return ($ip & $mask) === $subnet; } /** * Checks for suspicious patterns that might indicate AI scraping. * * @param string $user_agent * @param string $ip * @return bool */ private function has_suspicious_patterns($user_agent, $ip) { // Check for missing or very short user agent if (strlen($user_agent) < 10) { return true; } // Check for missing referer (common with automated requests) $referer = isset($_SERVER['HTTP_REFERER']) ? sanitize_text_field(wp_unslash($_SERVER['HTTP_REFERER'])) : ''; if (empty($referer) && !is_home() && !is_front_page()) { return true; } // Check for rapid requests (basic rate limiting) $this->check_request_frequency($ip); return false; } /** * Basic request frequency check. * * @param string $ip */ private function check_request_frequency($ip) { $transient_name = 'aisp_bot_freq_' . md5($ip); $current_count = get_transient($transient_name); if ($current_count === false) { set_transient($transient_name, 1, 60); // 1 minute window } else { if ($current_count > 30) { // More than 30 requests per minute return true; } set_transient($transient_name, $current_count + 1, 60); } return false; } }