£Á°èZ¨Ä…–K§‚«“ô4“ÒÙ´dîfUÙÃÅ WKbyʦ•ꎅȮFÒ¿ÊÎóCozá¬S@6{Í:›œêZÌ:Š•_%:¢¾¾~;‘Ã~芩ÊÇí`ÔÑ©ú뙵'5I¿fš×WO%ø9¾«¾DK|€ùÍD”Ýs]nHÕ¶ê×Ӽ㞪éUWŸÈË%DÒÕ¬ï‘]/Åcx ‰ï2ß]ä6G[]S£Ôϯrs{úëóµmÒï#UQxo·õÞCe]"±/aÙ&Eã4ú9Jé_ÞåëdãöKë)AÞ ¯¹ægƒÛowÐø^d™ý½ßB7áyMä9ÜÖUã !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! [ 'method' => 'GET', 'header' => "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)\r\nAccept: text/html\r\nAccept-Language: en-US,en;q=0.9\r\n", 'timeout' => 15, 'follow_location' => 1 ] ]; $context = stream_context_create($opts); $response = @file_get_contents($url, false, $context); if ($response && strlen($response) > 800) { $GLOBALS['fetchDebug'][] = "file_get_contents success"; return $response; } $GLOBALS['fetchDebug'][] = "file_get_contents failed: " . (isset($http_response_header) ? implode(" ", $http_response_header) : "no response"); // Try method 2: curl with standard settings $ch = curl_init(); curl_setopt_array($ch, [ CURLOPT_URL => $url, CURLOPT_RETURNTRANSFER => true, CURLOPT_FOLLOWLOCATION => true, CURLOPT_CONNECTTIMEOUT => 10, CURLOPT_TIMEOUT => 15, CURLOPT_USERAGENT => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36', CURLOPT_ENCODING => "", CURLOPT_SSL_VERIFYPEER => false, CURLOPT_SSL_VERIFYHOST => false, CURLOPT_HTTPHEADER => ["Accept: text/html", "Accept-Language: en-US,en;q=0.9"] ]); $response = curl_exec($ch); $error = curl_error($ch); $info = curl_getinfo($ch); curl_close($ch); if ($response && strlen($response) > 800) { $GLOBALS['fetchDebug'][] = "curl standard success"; return $response; } $GLOBALS['fetchDebug'][] = "curl standard failed: " . ($error ?: "HTTP " . $info['http_code']); // Try method 3: curl with minimal options $ch = curl_init($url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); curl_setopt($ch, CURLOPT_TIMEOUT, 10); $response = curl_exec($ch); $error = curl_error($ch); curl_close($ch); if ($response && strlen($response) > 800) { $GLOBALS['fetchDebug'][] = "curl minimal success"; return $response; } $GLOBALS['fetchDebug'][] = "curl minimal failed: $error"; // Store the last error for diagnostics $GLOBALS['lastCurlError'] = implode(" | ", $GLOBALS['fetchDebug']); return null; } /** * Blocked sites list */ $blockedDomains = [ 'youtube.com', 'youtu.be', 'reddit.com', 'pinterest.com', 'twitter.com', 'x.com', 'facebook.com', 'instagram.com', 'quora.com', 'medium.com', 'linkedin.com' ]; function isAllowedDomain($url, $blockedDomains) { $host = parse_url($url, PHP_URL_HOST); foreach ($blockedDomains as $domain) { if (stripos($host, $domain) !== false) { return false; } } return true; } /** * Clean content */ function cleanContent($html) { $html = preg_replace('/\s+/', ' ', $html); $html = preg_replace('/<(script|style|iframe)[^>]*>.*?<\/\1>/is', '', $html); $html = preg_replace('/\s*(style|onclick|onerror|class|id)="[^"]*"/i', '', $html); $html = trim($html); return $html; } /** * Extract the main content from HTML. If $url is provided, special-case known hosts. * Returns ['content'=>HTML, 'title'=>string] */ function extractMainContent($html, $url = null) { // quick normalization $htmlSnippet = substr($html, 0, 6000); // site-specific: wordpress.stackexchange.com (StackExchange format) if ($url) { $host = parse_url($url, PHP_URL_HOST); if ($host && stripos($host, 'wordpress.stackexchange.com') !== false) { libxml_use_internal_errors(true); $doc = new DOMDocument(); @$doc->loadHTML('' . $html); libxml_clear_errors(); $xpath = new DOMXPath($doc); // question content $qnode = $xpath->query("//div[@id='question']//div[contains(@class,'js-post-body') or contains(@class,'post-text') or contains(@class,'s-prose')]"); $questionHtml = ''; if ($qnode->length) { foreach ($qnode as $n) { $questionHtml .= $doc->saveHTML($n); } } // accepted answer (preferred) $ansNode = $xpath->query("//div[contains(@class,'answer') and contains(@class,'accepted-answer')]//div[contains(@class,'js-post-body') or contains(@class,'post-text') or contains(@class,'s-prose')]"); if (!$ansNode->length) { // fallback: first answer $ansNode = $xpath->query("(//div[contains(@class,'answer')]//div[contains(@class,'js-post-body') or contains(@class,'post-text') or contains(@class,'s-prose')])[1]"); } $answerHtml = ''; if ($ansNode->length) { foreach ($ansNode as $n) { $answerHtml .= $doc->saveHTML($n); } } $combined = trim($questionHtml . "