class-restai-search.php
1 <?php 2 /** 3 * AI-powered site search. When enabled, intercepts the standard WP search 4 * query and asks the Support Bot project for an answer + relevant sources. 5 * 6 * The native search results page is augmented with the AI answer at the top. 7 * 8 * @package RESTai 9 */ 10 11 namespace RESTai; 12 13 if ( ! defined( 'ABSPATH' ) ) { 14 exit; 15 } 16 17 class Search { 18 19 /** @var Client */ 20 private $client; 21 22 public function __construct( Client $client ) { 23 $this->client = $client; 24 add_action( 'pre_get_posts', array( $this, 'noop' ) ); 25 add_filter( 'the_content', array( $this, 'inject_ai_answer' ), 5 ); 26 } 27 28 public function noop( $q ) { 29 // reserved for future query rewrites 30 } 31 32 public function inject_ai_answer( $content ) { 33 $settings = get_option( 'restai_settings', array() ); 34 if ( empty( $settings['enable_search'] ) ) { 35 return $content; 36 } 37 if ( ! is_search() ) { 38 return $content; 39 } 40 static $injected = false; 41 if ( $injected ) { 42 return $content; 43 } 44 $injected = true; 45 46 $q = get_search_query(); 47 if ( '' === $q ) { 48 return $content; 49 } 50 51 $plugin = Plugin::instance(); 52 $project_id = $plugin->provisioner->project_for( 'support_bot' ); 53 if ( $project_id <= 0 ) { 54 return $content; 55 } 56 57 $key = 'restai_search_' . md5( $q ); 58 $cached = get_transient( $key ); 59 if ( false === $cached ) { 60 $ans = $this->client->ask( $project_id, $q ); 61 $cached = is_wp_error( $ans ) ? '' : $ans; 62 set_transient( $key, $cached, HOUR_IN_SECONDS ); 63 } 64 if ( '' === $cached ) { 65 return $content; 66 } 67 $panel = sprintf( 68 '<div class="restai-ai-answer" style="background:#f6f7f9;border:1px solid #e0e0e6;border-radius:8px;padding:16px;margin-bottom:16px;"><strong>%s</strong><div style="margin-top:8px;">%s</div></div>', 69 esc_html__( 'AI answer', 'restai' ), 70 wp_kses_post( $cached ) 71 ); 72 return $panel . $content; 73 } 74 }