/** * Helper function to add admin notices. * Note: This is a basic implementation. A more robust solution might use a dedicated class * or the standard WordPress admin notices hooks if notices need to persist across page loads. */ private function add_admin_notice( $message, $type = 'info' ) { // Simple echo for immediate display on the current page load // Types: 'info', 'warning', 'error', 'success' echo '

' . esc_html( $message ) . '

'; } /** * Fetches content from a URL and extracts the main text. * Returns the extracted text (string) on success, or WP_Error on failure. */ private function fetch_and_extract_content_from_url( $url ) { if ( ! filter_var( $url, FILTER_VALIDATE_URL ) ) { return new WP_Error( 'invalid_url', 'Erro: O Link fornecido não é uma URL válida.' ); } // Increase timeout for potentially slow external requests $args = [ 'timeout' => 30, // 30 seconds timeout 'user-agent' => 'WordPress/' . get_bloginfo( 'version' ) . '; ' . get_bloginfo( 'url' ) // Set a user agent ]; $response = wp_remote_get( $url, $args ); if ( is_wp_error( $response ) ) { return new WP_Error( 'fetch_failed', 'Erro ao buscar conteúdo do link: ' . $response->get_error_message() ); } $status_code = wp_remote_retrieve_response_code( $response ); if ( $status_code < 200 || $status_code >= 300 ) { return new WP_Error( 'fetch_bad_status', 'Erro ao buscar conteúdo do link: Status HTTP ' . $status_code ); } $content_type = wp_remote_retrieve_header( $response, 'content-type' ); if ( $content_type && stripos( $content_type, 'text/html' ) === false ) { return new WP_Error( 'fetch_not_html', 'Erro: O link não parece ser uma página HTML (Content-Type: ' . esc_html($content_type) . ').' ); } $body = wp_remote_retrieve_body( $response ); if ( empty( $body ) ) { return new WP_Error( 'fetch_empty_body', 'Erro: O corpo da resposta do link está vazio.' ); } // Basic content extraction: Strip tags and decode entities // This is a very rudimentary approach. More sophisticated extraction might be needed // for complex pages (e.g., using DOMDocument or a dedicated library). // For now, we'll rely on the AI to make sense of the stripped text. $text_content = wp_strip_all_tags( $body ); $text_content = html_entity_decode( $text_content ); // Decode HTML entities like & $text_content = preg_replace( '/\s+/', ' ', $text_content ); // Replace multiple whitespace chars with a single space // Limit the amount of text sent to the keyword extraction prompt (e.g., first 5000 chars) $max_chars_for_keyword = 5000; $trimmed_text = mb_substr( trim( $text_content ), 0, $max_chars_for_keyword ); if ( empty( $trimmed_text ) ) { return new WP_Error( 'extraction_failed', 'Erro: Não foi possível extrair texto útil do conteúdo do link.' ); } return $trimmed_text; } /** * Sends extracted text to AI to get a focus keyword. * Returns the keyword (string) on success, or WP_Error on failure. */ private function get_keyword_from_content( $text_content, $s ) { if ( empty( $s['api_key'] ) ) { return new WP_Error( 'missing_api_key', 'Erro: Chave de API não configurada.' ); } $prompt = sprintf( "Analise o seguinte texto e sugira uma palavra-chave foco curta e precisa (2-5 palavras) para um novo artigo de blog sobre o mesmo tema principal. Retorne APENAS a palavra-chave sugerida, sem nenhuma outra formatação ou texto adicional.\n\nTexto para Análise:\n---\n%s\n---", $text_content ); $api_url = 'https://api.openai.com/v1/chat/completions'; // Replace with actual API endpoint if different $headers = [ 'Authorization' => 'Bearer ' . $s['api_key'], 'Content-Type' => 'application/json', ]; $body = json_encode( [ 'model' => $s['model'] ?? 'gpt-4.1-mini', // Use configured model 'messages' => [ [ 'role' => 'system', 'content' => 'Você é um assistente especialista em SEO que identifica a palavra-chave principal de um texto.' ], [ 'role' => 'user', 'content' => $prompt ] ], 'max_tokens' => 20, // Keep low for just the keyword 'temperature' => 0.2, // Low temperature for focused result ] ); $response = wp_remote_post( $api_url, [ 'headers' => $headers, 'body' => $body, 'timeout' => 60, // Longer timeout for AI request ] ); if ( is_wp_error( $response ) ) { return new WP_Error( 'api_request_failed', 'Erro na comunicação com a API para obter palavra-chave: ' . $response->get_error_message() ); } $response_body = wp_remote_retrieve_body( $response ); $result = json_decode( $response_body, true ); if ( json_last_error() !== JSON_ERROR_NONE || ! isset( $result['choices'][0]['message']['content'] ) ) { $api_error = $result['error']['message'] ?? $response_body; return new WP_Error( 'api_invalid_response_keyword', 'Erro: Resposta inválida da API ao obter palavra-chave. Detalhes: ' . esc_html( $api_error ) ); } $suggested_keyword = trim( $result['choices'][0]['message']['content'] ); // Basic cleanup: remove quotes if AI added them $suggested_keyword = trim( $suggested_keyword, '"\'' ); if ( empty( $suggested_keyword ) ) { return new WP_Error( 'api_empty_keyword', 'Erro: A API não retornou uma palavra-chave sugerida.' ); } // Sanitize the keyword returned by the AI return sanitize_text_field( $suggested_keyword ); }