.tsv and serves the static file exactly as the server would. declare(strict_types=1); const LOG_DIR = '/home/u252205681/maraseel-app/ai-log'; const BOTS = [ 'ChatGPT-User' => 'ChatGPT (فتحها لمستخدم)', 'OAI-SearchBot' => 'ChatGPT Search', 'GPTBot' => 'OpenAI GPTBot', 'Claude-User' => 'Claude (فتحها لمستخدم)', 'Claude-SearchBot' => 'Claude Search', 'ClaudeBot' => 'Anthropic ClaudeBot', 'anthropic-ai' => 'Anthropic', 'Perplexity-User' => 'Perplexity (فتحها لمستخدم)', 'PerplexityBot' => 'PerplexityBot', 'Google-CloudVertexBot' => 'Google Vertex AI', 'GoogleOther' => 'GoogleOther', 'Applebot' => 'Applebot', 'meta-externalfetcher' => 'Meta AI (فتحها لمستخدم)', 'meta-externalagent' => 'Meta AI', 'MistralAI-User' => 'Mistral', 'DuckAssistBot' => 'DuckDuckGo AI', 'Amazonbot' => 'Amazonbot', 'Bytespider' => 'ByteDance', 'CCBot' => 'Common Crawl', 'cohere-ai' => 'Cohere', 'YouBot' => 'You.com', 'DeepSeek' => 'DeepSeek', 'Grok' => 'Grok', 'xAI' => 'Grok', ]; const REFS = [ 'chatgpt' => 'ChatGPT', 'openai' => 'ChatGPT', 'perplexity' => 'Perplexity', 'claude' => 'Claude', 'gemini' => 'Gemini', 'copilot' => 'Copilot', 'deepseek' => 'DeepSeek', 'grok' => 'Grok', 'meta.ai' => 'Meta AI', 'mistral' => 'Mistral', 'you.com' => 'You.com', 'poe.com' => 'Poe', ]; $uri = $_SERVER['REQUEST_URI'] ?? '/'; $path = rawurldecode(parse_url($uri, PHP_URL_PATH) ?: '/'); if ($path === '/_ai.php') { http_response_code(404); readfile(__DIR__ . '/404.html'); exit; } $file = __DIR__ . (str_ends_with($path, '/') ? $path . 'index.html' : $path); $real = realpath($file); $ok = !str_contains($path, '..') && $real !== false && str_starts_with($real, __DIR__ . '/') && is_file($real) && !str_ends_with($real, '.php'); $status = $ok ? 200 : 404; if (!$ok) $real = __DIR__ . '/404.html'; $ua = $_SERVER['HTTP_USER_AGENT'] ?? ''; $ref = $_SERVER['HTTP_REFERER'] ?? ''; $who = null; $kind = 'bot'; foreach (BOTS as $k => $name) if (stripos($ua, $k) !== false) { $who = $name; break; } if ($who === null) { $kind = 'ref'; $src = strtolower((parse_url($ref, PHP_URL_HOST) ?: '') . ' ' . (preg_match('/(?:^|&)utm_source=([^&]+)/i', $_SERVER['QUERY_STRING'] ?? '', $m) ? urldecode($m[1]) : '')); foreach (REFS as $k => $name) if (str_contains($src, $k)) { $who = $name; break; } $who ??= 'AI'; } $mtime = filemtime($real) ?: time(); $since = strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE'] ?? '') ?: 0; if ($status === 200 && $since >= $mtime) $status = 304; $clean = fn(string $s): string => mb_substr(str_replace(["\t", "\n", "\r"], ' ', $s), 0, 200); $line = implode("\t", [date('c'), $kind, $who, $clean($path), $status, $kind === 'bot' ? ($_SERVER['REMOTE_ADDR'] ?? '') : '', $clean($kind === 'bot' ? $ua : $ref)]) . "\n"; if (is_dir(LOG_DIR) || @mkdir(LOG_DIR, 0700, true)) @file_put_contents(LOG_DIR . '/' . date('Y-m') . '.tsv', $line, FILE_APPEND | LOCK_EX); $types = ['html' => 'text/html; charset=utf-8', 'txt' => 'text/plain; charset=utf-8', 'xml' => 'application/xml; charset=utf-8']; http_response_code($status); header('Content-Type: ' . ($types[strtolower(pathinfo($real, PATHINFO_EXTENSION))] ?? 'application/octet-stream')); header('Cache-Control: public, max-age=0, must-revalidate'); header('Last-Modified: ' . gmdate('D, d M Y H:i:s', $mtime) . ' GMT'); if ($status === 304 || ($_SERVER['REQUEST_METHOD'] ?? 'GET') === 'HEAD') exit; readfile($real);