esc_url_raw( admin_url( 'admin-ajax.php' ) ), 'nonce' => wp_create_nonce( 'adamca_admin_nonce' ), 'savedModel' => get_option( 'adamca_ai_model', '' ), 'i18n' => array( 'working' => __( 'Working...', 'adams-crypto-analysis' ), 'success' => __( 'Success!', 'adams-crypto-analysis' ), 'errorOccurred' => __( 'Error occurred.', 'adams-crypto-analysis' ), 'requestFailed' => __( 'Request failed: ', 'adams-crypto-analysis' ), ), ) ); } /** * Add the settings page under the Settings menu. */ public static function add_settings_page() { add_options_page( __( 'Adams Crypto Analysis', 'adams-crypto-analysis' ), __( 'Adams Crypto Analysis', 'adams-crypto-analysis' ), 'manage_options', 'adams-crypto-analysis', array( __CLASS__, 'render_settings_page' ) ); } /** * Register all plugin settings. */ public static function register_settings() { $settings_group = 'adamca_settings_group'; register_setting( $settings_group, 'adamca_coingecko_api_key', array( 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field', 'default' => '', ) ); register_setting( $settings_group, 'adamca_ai_provider', array( 'type' => 'string', 'sanitize_callback' => array( __CLASS__, 'sanitize_provider' ), 'default' => 'openai', ) ); register_setting( $settings_group, 'adamca_ai_api_key', array( 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field', 'default' => '', ) ); register_setting( $settings_group, 'adamca_ai_model', array( 'type' => 'string', 'sanitize_callback' => array( __CLASS__, 'sanitize_model' ), 'default' => '', ) ); register_setting( $settings_group, 'adamca_top10_coins', array( 'type' => 'string', 'sanitize_callback' => array( __CLASS__, 'sanitize_coins_list' ), 'default' => '', ) ); register_setting( $settings_group, 'adamca_cache_expiry', array( 'type' => 'integer', 'sanitize_callback' => array( __CLASS__, 'sanitize_cache_expiry' ), 'default' => 43200, ) ); // CoinGecko section. add_settings_section( 'adamca_coingecko_section', __( 'CoinGecko Settings', 'adams-crypto-analysis' ), '__return_null', 'adams-crypto-analysis' ); add_settings_field( 'adamca_coingecko_api_key', __( 'CoinGecko API Key (Pro)', 'adams-crypto-analysis' ), array( __CLASS__, 'render_password_field' ), 'adams-crypto-analysis', 'adamca_coingecko_section', array( 'option_name' => 'adamca_coingecko_api_key', 'description' => __( 'Leave blank for free tier.', 'adams-crypto-analysis' ) ) ); // AI section. add_settings_section( 'adamca_ai_section', __( 'AI Provider Settings', 'adams-crypto-analysis' ), '__return_null', 'adams-crypto-analysis' ); add_settings_field( 'adamca_ai_provider', __( 'AI Provider', 'adams-crypto-analysis' ), array( __CLASS__, 'render_provider_field' ), 'adams-crypto-analysis', 'adamca_ai_section' ); add_settings_field( 'adamca_ai_api_key', __( 'AI API Key', 'adams-crypto-analysis' ), array( __CLASS__, 'render_password_field' ), 'adams-crypto-analysis', 'adamca_ai_section', array( 'option_name' => 'adamca_ai_api_key', 'description' => __( 'API key for the selected provider.', 'adams-crypto-analysis' ) ) ); add_settings_field( 'adamca_ai_model', __( 'AI Model', 'adams-crypto-analysis' ), array( __CLASS__, 'render_model_field' ), 'adams-crypto-analysis', 'adamca_ai_section' ); // Cache section. add_settings_section( 'adamca_cache_section', __( 'Cache Settings', 'adams-crypto-analysis' ), '__return_null', 'adams-crypto-analysis' ); add_settings_field( 'adamca_cache_expiry', __( 'Cache Expiry (seconds)', 'adams-crypto-analysis' ), array( __CLASS__, 'render_number_field' ), 'adams-crypto-analysis', 'adamca_cache_section', array( 'option_name' => 'adamca_cache_expiry', 'description' => __( 'How long to cache analysis results. Default: 43200 (12 hours). Minimum: 3600 (1 hour).', 'adams-crypto-analysis' ) ) ); add_settings_field( 'adamca_top10_coins', __( 'Top 10 Coins', 'adams-crypto-analysis' ), array( __CLASS__, 'render_textarea_field' ), 'adams-crypto-analysis', 'adamca_cache_section', array( 'option_name' => 'adamca_top10_coins', 'description' => __( 'CoinGecko IDs, one per line. These get a lightning bolt in the frontend dropdown.', 'adams-crypto-analysis' ) ) ); } /** * Render the settings page. */ public static function render_settings_page() { if ( ! current_user_can( 'manage_options' ) ) { return; } ?>
<?php esc_attr_e( 'Adams Crypto Analysis', 'adams-crypto-analysis' ); ?>

[adamca_crypto_analysis]



'OpenAI', 'xai' => 'xAI / Grok', 'anthropic' => 'Anthropic / Claude', ); ?> array( 'gpt-5', 'gpt-5-mini', 'gpt-4o', 'gpt-4o-mini' ), 'xai' => array( 'grok-4-fast-non-reasoning', 'grok-4', 'grok-3' ), 'anthropic' => array( 'claude-opus-4-5', 'claude-sonnet-4-5' ), ); ?>

15, 'headers' => $headers_arr, ) ); if ( is_wp_error( $response ) ) { wp_send_json_error( $response->get_error_message() ); } $status_code = wp_remote_retrieve_response_code( $response ); if ( 200 === $status_code ) { wp_send_json_success( array( 'message' => __( 'CoinGecko connection successful!', 'adams-crypto-analysis' ) ) ); } else { wp_send_json_error( sprintf( /* translators: %d: HTTP status code */ __( 'CoinGecko returned HTTP %d.', 'adams-crypto-analysis' ), $status_code ) ); } } /** * AJAX handler: Test AI provider connection. */ public static function ajax_test_ai_provider() { check_ajax_referer( 'adamca_admin_nonce', 'nonce' ); if ( ! current_user_can( 'manage_options' ) ) { wp_send_json_error( __( 'Unauthorized.', 'adams-crypto-analysis' ) ); } $provider = get_option( 'adamca_ai_provider', 'openai' ); $api_key = get_option( 'adamca_ai_api_key', '' ); $model_name = get_option( 'adamca_ai_model', '' ); if ( empty( $api_key ) ) { wp_send_json_error( __( 'AI API key is not configured.', 'adams-crypto-analysis' ) ); } $test_prompt = 'Reply with exactly the word OK.'; $system_text = 'You are a helpful assistant.'; switch ( $provider ) { case 'xai': $effective_model = $model_name ?: 'grok-3'; $xai_body = array( 'model' => $effective_model, 'messages' => array( array( 'role' => 'system', 'content' => $system_text ), array( 'role' => 'user', 'content' => $test_prompt ), ), 'max_tokens' => 10, 'temperature' => 0, 'stream' => false, ); $request_url = 'https://api.x.ai/v1/chat/completions'; $request_body = wp_json_encode( $xai_body ); $headers_arr = array( 'Content-Type' => 'application/json', 'Authorization' => 'Bearer ' . $api_key, ); break; case 'anthropic': $request_url = 'https://api.anthropic.com/v1/messages'; $request_body = wp_json_encode( array( 'model' => $model_name ?: 'claude-opus-4-5', 'max_tokens' => 10, 'system' => $system_text, 'messages' => array( array( 'role' => 'user', 'content' => $test_prompt ), ), ) ); $headers_arr = array( 'Content-Type' => 'application/json', 'x-api-key' => $api_key, 'anthropic-version' => '2023-06-01', ); break; case 'openai': default: $effective_model = $model_name ?: 'gpt-4o'; if ( 0 === strpos( $effective_model, 'gpt-5' ) ) { $request_url = 'https://api.openai.com/v1/responses'; $combined_prompt = $system_text . "\n\n" . $test_prompt; $request_body = wp_json_encode( array( 'model' => $effective_model, 'input' => array( array( 'role' => 'user', 'content' => array( array( 'type' => 'input_text', 'text' => $combined_prompt, ), ), ), ), 'max_output_tokens' => 16, ) ); } else { $request_url = 'https://api.openai.com/v1/chat/completions'; $request_body = wp_json_encode( array( 'model' => $effective_model, 'messages' => array( array( 'role' => 'system', 'content' => $system_text ), array( 'role' => 'user', 'content' => $test_prompt ), ), 'max_tokens' => 10, 'temperature' => 0, ) ); } $headers_arr = array( 'Content-Type' => 'application/json', 'Authorization' => 'Bearer ' . $api_key, ); break; } $response = wp_remote_post( $request_url, array( 'timeout' => 30, 'headers' => $headers_arr, 'body' => $request_body, ) ); if ( is_wp_error( $response ) ) { wp_send_json_error( $response->get_error_message() ); } $status_code = wp_remote_retrieve_response_code( $response ); if ( 200 === $status_code ) { wp_send_json_success( array( 'message' => sprintf( /* translators: %s: AI provider name */ __( '%s connection successful!', 'adams-crypto-analysis' ), ucfirst( $provider ) ), ) ); } else { $error_body = wp_remote_retrieve_body( $response ); $safe_error = sanitize_text_field( substr( $error_body, 0, 200 ) ); wp_send_json_error( sprintf( /* translators: 1: AI provider name, 2: HTTP status code, 3: truncated error message */ __( '%1$s returned HTTP %2$d: %3$s', 'adams-crypto-analysis' ), ucfirst( $provider ), $status_code, $safe_error ) ); } } /** * AJAX handler: Clear cache for a coin or all coins. */ public static function ajax_clear_cache() { check_ajax_referer( 'adamca_admin_nonce', 'nonce' ); if ( ! current_user_can( 'manage_options' ) ) { wp_send_json_error( __( 'Unauthorized.', 'adams-crypto-analysis' ) ); } $coin_id = isset( $_POST['coin_id'] ) ? sanitize_key( wp_unslash( $_POST['coin_id'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Missing -- nonce verified above. if ( empty( $coin_id ) ) { wp_send_json_error( __( 'No coin ID provided.', 'adams-crypto-analysis' ) ); } if ( 'all' === $coin_id ) { $clear_count = ADAMCA_Cache::clear_all_cache(); wp_send_json_success( array( 'message' => sprintf( /* translators: %d: number of coins cleared */ __( 'Cleared cache for %d coin(s).', 'adams-crypto-analysis' ), $clear_count ), ) ); } else { ADAMCA_Cache::clear_cache( $coin_id ); wp_send_json_success( array( 'message' => sprintf( /* translators: %s: coin ID */ __( 'Cleared cache for %s.', 'adams-crypto-analysis' ), $coin_id ), ) ); } } }