generate_api_key(); update_option( self::API_KEY_OPTION, $key, false ); } return $key; } /** * Submit a single URL to IndexNow. * * @param string $url The URL to submit. * * @return bool True on success. */ public function submit_url( string $url ): bool { $api_key = $this->get_api_key(); $host = wp_parse_url( home_url(), PHP_URL_HOST ); $key_location = home_url( '/' . $api_key . '.txt' ); $response = wp_remote_post( self::INDEXNOW_ENDPOINT, array( 'headers' => array( 'Content-Type' => 'application/json; charset=utf-8' ), 'body' => wp_json_encode( array( 'host' => $host, 'key' => $api_key, 'keyLocation' => $key_location, 'urlList' => array( $url ), ) ), 'timeout' => 15, ) ); if ( is_wp_error( $response ) ) { return false; } $code = wp_remote_retrieve_response_code( $response ); return $code >= 200 && $code < 300; } /** * Submit multiple URLs to IndexNow. * * @param array $urls Array of URLs to submit. * * @return bool True on success. */ public function submit_urls( array $urls ): bool { if ( empty( $urls ) ) { return false; } $api_key = $this->get_api_key(); $host = wp_parse_url( home_url(), PHP_URL_HOST ); $key_location = home_url( '/' . $api_key . '.txt' ); $response = wp_remote_post( self::INDEXNOW_ENDPOINT, array( 'headers' => array( 'Content-Type' => 'application/json; charset=utf-8' ), 'body' => wp_json_encode( array( 'host' => $host, 'key' => $api_key, 'keyLocation' => $key_location, 'urlList' => array_values( $urls ), ) ), 'timeout' => 15, ) ); if ( is_wp_error( $response ) ) { return false; } $code = wp_remote_retrieve_response_code( $response ); return $code >= 200 && $code < 300; } /** * Notify Bing that the feed has been updated. * * Submits the feed URL via IndexNow. * * @param string $feed_url The feed URL that was updated. * * @return bool True on success. */ public function notify_feed_updated( string $feed_url ): bool { return $this->submit_url( $feed_url ); } /** * Serve the IndexNow key verification file content. * * Should be called on template_redirect when the request matches * /{api_key}.txt to verify key ownership. * * @return void */ public function maybe_serve_key_file(): void { $request_uri = isset( $_SERVER['REQUEST_URI'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : ''; $api_key = $this->get_api_key(); if ( '/' . $api_key . '.txt' === $request_uri ) { header( 'Content-Type: text/plain; charset=utf-8' ); header( 'Cache-Control: public, max-age=86400' ); echo esc_html( $api_key ); exit; } } /** * Schedule a daily IndexNow ping for the Bing feed. * * @return void */ public function schedule_daily_ping(): void { $hook = 'aicommerce_indexnow_ping'; if ( ! wp_next_scheduled( $hook ) ) { wp_schedule_event( time(), 'daily', $hook ); } } /** * Generate a random 32-character hex key for IndexNow. * * @return string The generated key. */ private function generate_api_key(): string { return bin2hex( random_bytes( 16 ) ); } }