function mh_free_search_handler( WP_REST_Request $req ) { $p = $req->get_json_params(); $q = isset($p['q']) ? trim(wp_strip_all_tags((string)$p['q'])) : ''; if ($q === '') { return new WP_REST_Response([ 'answer' => 'Please type a question or keywords (e.g., “free trial”, “packages”, “documents”, “Perak houses”).', 'results' => [], ], 200); } // ---- Local search (pages & posts) ---- $allowed_local_types = ['page','post']; $local = []; $q1 = new WP_Query([ 's' => $q, 'post_status' => 'publish', 'post_type' => $allowed_local_types, 'posts_per_page' => 5, 'ignore_sticky_posts' => true, 'no_found_rows' => true, ]); if ($q1->have_posts()) { while ($q1->have_posts()) { $q1->the_post(); $local[] = [ 'title' => html_entity_decode(get_the_title(), ENT_QUOTES, 'UTF-8'), 'url' => get_permalink(), ]; } wp_reset_postdata(); } // ---- Remote search (only our 3 domains) ---- $domains = [ 'https://malaysiahome.my', 'https://bilikku.my', 'https://bilikkusg.com', ]; $remote = []; foreach ($domains as $base) { $url = rtrim($base, '/') . '/wp-json/wp/v2/search?search=' . rawurlencode($q) . '&per_page=5'; $resp = wp_remote_get($url, ['timeout' => 10]); if (is_wp_error($resp)) continue; if (wp_remote_retrieve_response_code($resp) !== 200) continue; $data = json_decode(wp_remote_retrieve_body($resp), true); if (!is_array($data)) continue; foreach ($data as $row) { $title = isset($row['title']) ? (string)$row['title'] : ''; $link = isset($row['url']) ? (string)$row['url'] : ''; if ($title === '' || $link === '') continue; if (preg_match('~\?(?:elementor_|attachment_id=|p=|page_id=)~i', $link)) continue; if (strpos($link, '/wp-admin/') !== false) continue; $link = preg_replace('~/+$~', '/', $link); $remote[] = [ 'title' => html_entity_decode($title, ENT_QUOTES, 'UTF-8'), 'url' => $link, ]; } } // ---- Merge + de-dup ---- $all = array_merge($local, $remote); $seen = []; $clean = []; foreach ($all as $r) { $k = strtolower($r['url']); if (isset($seen[$k])) continue; $seen[$k] = true; $clean[] = $r; if (count($clean) >= 8) break; } // ---- Friendly answer (mini intent layer) ---- $t = strtolower($q); $intro = ''; if (preg_match('~(free\s*trial|trial)~', $t)) { $intro = 'Here’s what we offer for the free trial and packages:'; } elseif (preg_match('~(package|pricing|price|plan)~', $t)) { $intro = 'Here are our packages and pricing details:'; } elseif (preg_match('~(document|docs|requirement|ic|passport)~', $t)) { $intro = 'These pages explain the documents and requirements:'; } elseif (preg_match('~(book|booking|appointment|schedule|apply)~', $t)) { $intro = 'To book or get started, the following pages will help:'; } elseif (preg_match('~(area|coverage|state|city|perak|selangor|penang|johor)~', $t)) { $intro = 'These pages cover locations and listings for that area:'; } else { $intro = 'Here’s the most relevant information I found:'; } if (empty($clean)) { $answer = "I couldn’t find a public page for “{$q}” on our sites. " . "Try keywords like “packages”, “free trial”, “documents”, “booking steps”, or a location (e.g., “Perak”)."; } else { // Choose one “best” match (simple heuristic: title contains first keyword) $best = $clean[0]; $firstKw = preg_split('~\s+~', $t, 2)[0] ?? ''; foreach ($clean as $item) { if ($firstKw && strpos(strtolower($item['title']), $firstKw) !== false) { $best = $item; break; } } $more = array_values(array_filter($clean, fn($x) => $x['url'] !== $best['url'])); $more = array_slice($more, 0, 3); $lines = array_map(function($r){ return "• {$r['title']} — {$r['url']}"; }, $more); $answer = "{$intro}\nBest match: {$best['title']} — {$best['url']}"; if ($lines) $answer .= "\nRelated:\n" . implode("\n", $lines); } return new WP_REST_Response([ 'answer' => $answer, 'results' => $clean, ], 200); }