'/GPTBot/i', 'OAI-SearchBot' => '/OAI-SearchBot/i', 'ChatGPT-User' => '/ChatGPT-User/i', 'ClaudeBot' => '/ClaudeBot/i', 'Claude-SearchBot' => '/Claude-SearchBot/i', 'Claude-User' => '/Claude-User/i', 'Google-Extended' => '/Google-Extended/i', 'GoogleOther' => '/GoogleOther/i', 'PerplexityBot' => '/PerplexityBot/i', 'Perplexity-User' => '/Perplexity-User/i', 'Meta-ExternalAgent' => '/Meta-ExternalAgent/i', 'Bytespider' => '/Bytespider/i', 'CCBot' => '/CCBot/i', 'Applebot-Extended' => '/Applebot-Extended/i', 'Amazonbot' => '/Amazonbot/i', 'Diffbot' => '/Diffbot/i', 'Cohere-ai' => '/cohere-ai/i', 'FacebookBot' => '/FacebookBot/i', ]; public function __construct() { $settings = AI_Ready_WP::get_settings(); if ( empty( $settings['enable_crawler_analytics'] ) ) { return; } // Track on frontend only, skip admin/cron if ( ! is_admin() && ! wp_doing_cron() && ! wp_doing_ajax() ) { add_action( 'wp', [ $this, 'track_visit' ] ); } } /** * Track an AI bot visit. */ public function track_visit(): void { $ua = isset( $_SERVER['HTTP_USER_AGENT'] ) ? sanitize_text_field( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ) : ''; if ( empty( $ua ) ) { return; } $bot = self::detect_bot( $ua ); if ( ! $bot ) { return; } // Update aggregate stats $stats = get_option( self::OPTION_KEY, [] ); if ( ! isset( $stats[ $bot ] ) ) { $stats[ $bot ] = [ 'visits' => 0, 'first_seen' => current_time( 'mysql' ), 'last_seen' => null, ]; } $stats[ $bot ]['visits']++; $stats[ $bot ]['last_seen'] = current_time( 'mysql' ); update_option( self::OPTION_KEY, $stats, false ); // Append to recent log (capped) $log = get_option( self::LOG_KEY, [] ); $log[] = [ 'bot' => $bot, 'url' => isset( $_SERVER['REQUEST_URI'] ) ? esc_url_raw( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : '/', 'time' => current_time( 'mysql' ), ]; // Keep only the last MAX_LOG entries if ( count( $log ) > self::MAX_LOG ) { $log = array_slice( $log, -self::MAX_LOG ); } update_option( self::LOG_KEY, $log, false ); } /** * Detect which AI bot is visiting based on User-Agent. */ public static function detect_bot( string $user_agent ): ?string { foreach ( self::BOT_PATTERNS as $name => $pattern ) { if ( preg_match( $pattern, $user_agent ) ) { return $name; } } return null; } /** * Get aggregate stats. */ public static function get_stats(): array { return get_option( self::OPTION_KEY, [] ); } /** * Get recent visit log. */ public static function get_log( int $limit = 50 ): array { $log = get_option( self::LOG_KEY, [] ); return array_slice( array_reverse( $log ), 0, $limit ); } /** * Get total visits across all bots. */ public static function get_total_visits(): int { $stats = self::get_stats(); $total = 0; foreach ( $stats as $bot_stats ) { $total += $bot_stats['visits'] ?? 0; } return $total; } /** * Reset all analytics data. */ public static function reset(): void { delete_option( self::OPTION_KEY ); delete_option( self::LOG_KEY ); } }