/**
* 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 );
}