USER
<?php
// Evitar el acceso directo al archivo.
if (!defined('ABSPATH')) {
exit;
}
// Crear la página de administración.
function tag_create_admin_menu() {
add_menu_page(
'SharkContents Article Generator',
'SharkContents Article Generator',
'manage_options',
'SharkContents-article-generator',
'tag_admin_page',
'dashicons-admin-generic'
);
}
add_action('admin_menu', 'tag_create_admin_menu');
// Funciones para obtener los códigos de idioma para la API de Serper
function tag_get_gl_code() {
$languages = [
"Español" => "es",
"Portugués" => "br",
"Sueco" => "se",
"Indonesio" => "id",
"Italiano" => "it",
"Alemán" => "de",
"Hebreo" => "il",
"Japonés" => "jp",
"Coreano" => "kr",
"Chino Mandarin" => "cn",
"Inglés" => "us",
"Polaco" => "pl",
"Árabe" => "eg",
"Neerlandés" => "nl",
"Turco" => "tr",
"Francés" => "fr",
"Hindi" => "in",
"Ruso" => "ru",
"Vietnamita" => "vn",
"Sundanés" => "sd",
"Español (Perú)" => "pe",
"Español (México)" => "mx",
"Español (Argentina)" => "ar",
"Español (Chile)" => "cl",
"Español (Colombia)" => "co",
"Español (Ecuador)" => "ec",
"Español (Venezuela)" => "ve",
"Español (Guatemala)" => "gt",
"Español (Cuba)" => "cu",
"Español (Bolivia)" => "bo",
"Español (Haití)" => "ht",
"Español (República Dominicana)" => "do",
"Español (Honduras)" => "hn",
"Español (Paraguay)" => "py",
"Español (Nicaragua)" => "ni",
"Español (El Salvador)" => "sv",
"Español (Costa Rica)" => "cr",
"Español (Puerto Rico)" => "pr",
"Español (Panamá)" => "pa",
"Español (Uruguay)" => "uy",
"Español (Jamaica)" => "jm",
"Español (Trinidad y Tobago)" => "tt",
"Español (Belice)" => "bz"
];
$selected_language = get_option('tag_language', 'Español');
return $languages[$selected_language] ?? "es";
}
function tag_get_hl_code() {
$languages = [
"Español" => "es",
"Portugués" => "pt-br",
"Sueco" => "sv",
"Indonesio" => "id",
"Italiano" => "it",
"Alemán" => "de",
"Hebreo" => "iw",
"Japonés" => "ja",
"Coreano" => "ko",
"Chino Mandarin" => "zh-tw",
"Inglés" => "en",
"Polaco" => "pl",
"Árabe" => "ar",
"Neerlandés" => "de",
"Turco" => "tr",
"Francés" => "fr",
"Hindi" => "hi",
"Ruso" => "ru",
"Vietnamita" => "vi",
"Sundanés" => "su",
"Español (Perú)" => "es",
"Español (México)" => "es",
"Español (Argentina)" => "es",
"Español (Chile)" => "es",
"Español (Colombia)" => "es",
"Español (Ecuador)" => "es",
"Español (Venezuela)" => "es",
"Español (Guatemala)" => "es",
"Español (Cuba)" => "es",
"Español (Bolivia)" => "es",
"Español (Haití)" => "es",
"Español (República Dominicana)" => "es",
"Español (Honduras)" => "es",
"Español (Paraguay)" => "es",
"Español (Nicaragua)" => "es",
"Español (El Salvador)" => "es",
"Español (Costa Rica)" => "es",
"Español (Puerto Rico)" => "es",
"Español (Panamá)" => "es",
"Español (Uruguay)" => "es",
"Español (Jamaica)" => "es",
"Español (Trinidad y Tobago)" => "es",
"Español (Belice)" => "es"
];
$selected_language = get_option('tag_language', 'Español');
return $languages[$selected_language] ?? "es";
}
// Funciones para llamar a la API de Serper con cURL y timeout de 60 segundos
function tag_call_google_serp_dev($title) {
$api_keys = explode("\n", get_option('tag_serper_api_keys'));
$serper_api_key = $api_keys[array_rand($api_keys)];
if (empty($serper_api_key)) {
return null;
}
$url = "https://google.serper.dev/search";
$payload = json_encode([
"q" => $title,
"gl" => tag_get_gl_code(),
"hl" => tag_get_hl_code(),
"autocorrect" => true,
"page" => 1,
"type" => "search"
]);
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $payload,
CURLOPT_HTTPHEADER => [
'X-API-KEY: ' . $serper_api_key,
'Content-Type: application/json'
],
CURLOPT_TIMEOUT => 60 // Timeout de 60 segundos
]);
$response = curl_exec($ch);
$error = curl_error($ch);
curl_close($ch);
if ($error) {
error_log("Error en la llamada a la API de Serper (Search): " . $error);
return null;
}
$data = json_decode($response);
if (isset($data->peopleAlsoAsk)) {
return array_map(function($item) { return $item->question; }, $data->peopleAlsoAsk);
}
return null;
}
function tag_call_google_imagen_dev($title) {
$api_keys = explode("\n", get_option('tag_serper_api_keys'));
$serper_api_key = $api_keys[array_rand($api_keys)];
if (empty($serper_api_key)) {
return null;
}
$url = "https://google.serper.dev/images";
$payload = json_encode([
"q" => $title,
"gl" => tag_get_gl_code(),
"hl" => tag_get_hl_code(),
"autocorrect" => true,
"page" => 1,
"type" => "images",
"engine" => "google",
"num" => 1
]);
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $payload,
CURLOPT_HTTPHEADER => [
'X-API-KEY: ' . $serper_api_key,
'Content-Type: application/json'
],
CURLOPT_TIMEOUT => 60 // Timeout de 60 segundos
]);
$response = curl_exec($ch);
$error = curl_error($ch);
curl_close($ch);
if ($error) {
error_log("Error en la llamada a la API de Serper (Images): " . $error);
return null;
}
$data = json_decode($response);
if (isset($data->images) && isset($data->images[0]->imageUrl)) {
return $data->images[0]->imageUrl;
}
return null;
}
// Función para obtener el enlace embed del video de YouTube
function tag_call_google_serp_dev_videos($title) {
$api_keys = explode("\n", get_option('tag_serper_api_keys'));
$serper_api_key = $api_keys[array_rand($api_keys)];
if (empty($serper_api_key)) {
return null; // Si no hay API key, se omite la función
}
$url = "https://google.serper.dev/videos";
$payload = json_encode([
"q" => $title,
"gl" => tag_get_gl_code(),
"hl" => tag_get_hl_code(),
"autocorrect" => true,
"page" => 1,
"type" => "videos",
"engine" => "google"
]);
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $payload,
CURLOPT_HTTPHEADER => [
'X-API-KEY: ' . $serper_api_key,
'Content-Type: application/json'
],
CURLOPT_TIMEOUT => 60 // Timeout de 60 segundos
]);
$response = curl_exec($ch);
$error = curl_error($ch);
curl_close($ch);
if ($error) {
error_log("Error en la llamada a la API de Serper (Videos): " . $error);
return null; // Si hay error en la llamada, se omite la función
}
$data = json_decode($response);
$videos = $data->videos ?? []; // Obtener videos o un array vacío si no hay
// Regex para identificar el ID del video (11 caracteres alfanuméricos)
$video_id_regex = '/^[a-zA-Z0-9_-]{11}$/';
foreach ($videos as $video) {
$video_link = $video->link ?? '';
if (strpos($video_link, 'youtube.com/watch?v=') !== false) {
$video_id = explode("watch?v=", $video_link)[1];
$video_embed_link = "https://www.youtube.com/embed/$video_id";
// Verificar que el ID sea correcto y no sea una playlist
if (preg_match($video_id_regex, $video_id) &&
!preg_match('/\/embed\/playlist\?|\/watch\?/', $video_embed_link)) {
return $video_embed_link;
}
}
}
return null; // No se encontró ningún video válido
}
// Función para llamar a la API de Google Maps de Serper
function tag_call_google_maps_dev($title) {
$api_keys = explode("\n", get_option('tag_serper_api_keys'));
$serper_api_key = $api_keys[array_rand($api_keys)];
if (empty($serper_api_key)) {
return null;
}
$url = "https://google.serper.dev/maps";
$payload = json_encode([
"q" => $title,
"gl" => tag_get_gl_code(),
"hl" => tag_get_hl_code(),
"page" => 1,
"type" => "maps",
"engine" => "google"
]);
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $payload,
CURLOPT_HTTPHEADER => [
'X-API-KEY: ' . $serper_api_key,
'Content-Type: application/json'
],
CURLOPT_TIMEOUT => 60 // Timeout de 60 segundos
]);
$response = curl_exec($ch);
$error = curl_error($ch);
curl_close($ch);
if ($error) {
error_log("Error en la llamada a la API de Serper (Maps): " . $error);
return null;
}
$data = json_decode($response);
if (isset($data->places)) {
$places = [];
// Limitar la cantidad de lugares a 10
$places_data = array_slice($data->places, 0, 10);
foreach ($places_data as $place_data) {
$places[] = [
"title" => $place_data->title,
"address" => $place_data->address,
"rating" => $place_data->rating,
"phoneNumber" => $place_data->phoneNumber,
"website" => $place_data->website
];
}
return $places;
}
return null;
}
// Función para llamar a la API de Google Images de Serper para obtener imágenes de directorio
function tag_call_google_imagen_directorio_dev($title) {
$api_keys = explode("\n", get_option('tag_serper_api_keys'));
$serper_api_key = $api_keys[array_rand($api_keys)];
if (empty($serper_api_key)) {
return null;
}
$url = "https://google.serper.dev/images";
$payload = json_encode([
"q" => $title,
"gl" => tag_get_gl_code(),
"hl" => tag_get_hl_code(),
"autocorrect" => true,
"page" => 1,
"type" => "images",
"engine" => "google",
"num" => 10
]);
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $payload,
CURLOPT_HTTPHEADER => [
'X-API-KEY: ' . $serper_api_key,
'Content-Type: application/json'
],
CURLOPT_TIMEOUT => 60 // Timeout de 60 segundos
]);
$response = curl_exec($ch);
$error = curl_error($ch);
curl_close($ch);
if ($error) {
error_log("Error en la llamada a la API de Serper (Images Directorio): " . $error);
return null;
}
$data = json_decode($response);
if (isset($data->images)) {
$imagenes_filtradas = [];
$dominio_no_deseado = "/https:\/\/lookaside\.fbsbx\.com\/|https:\/\/lookaside\.instagram\.com\//";
$extensiones_validas = "/\.(png|jpg)$/";
foreach ($data->images as $imagen) {
if (!preg_match($dominio_no_deseado, $imagen->imageUrl)) {
$imagenes_filtradas[] = $imagen->imageUrl;
} elseif (!preg_match($extensiones_validas, $imagen->imageUrl)) {
$imagenes_filtradas[] = $imagen->thumbnailUrl;
}
}
if (!empty($imagenes_filtradas)) {
return $imagenes_filtradas[0];
} elseif (isset($data->images[0]->thumbnailUrl)) {
return $data->images[0]->thumbnailUrl;
}
}
return null;
}
function search_amazon($title) {
$api_keys = explode("\n", get_option('tag_scraping_dog_api_keys')); // Reemplaza 'tag_scraping_dog_api_keys' con la opción correcta de WordPress
// Verifica si no hay claves API
if (empty($api_keys)) {
return null;
}
$amazon_domains = [
"USA" => "com",
"España" => "es",
"Japón" => "co.jp",
"Alemania" => "de",
"Australia" => "com.au",
"Brasil" => "com.br",
"Canadá" => "ca",
"Francia" => "fr",
"India" => "in",
"Italia" => "it",
"México" => "com.mx",
"Países Bajos" => "nl",
"Arabia Saudita" => "sa",
"Egipto" => "eg",
"Singapur" => "sg",
"Turquía" => "com.tr",
"Emiratos Árabes Unidos" => "ae",
"Reino Unido" => "co.uk",
"Polonia" => "pl",
"Suecia" => "se"
];
$selected_country = get_option('tag_amazon_country'); // Reemplaza 'tag_amazon_country' con la opción correcta de WordPress
$api_url = "https://api.scrapingdog.com/amazon/search";
$domain = $amazon_domains[$selected_country] ?? "com"; // Usa "com" por defecto si no se encuentra el país
$query = $title;
$params = [
"domain" => $domain,
"page" => 1,
"query" => $query,
];
$retries = 0;
$max_retries = 3; // Cambiar el número de reintentos aquí
while (!empty($api_keys) && $retries < $max_retries) {
$dog_api = $api_keys[array_rand($api_keys)];
$params["api_key"] = $dog_api;
$url = $api_url . '?' . http_build_query($params);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 300); // Tiempo de espera de 300 segundos (5 minutos)
$response = curl_exec($ch);
$error = curl_error($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($error) {
// Manejar el error de cURL
error_log("Error en la solicitud a la API de ScrapingDog (cURL): " . $error);
$retries++;
continue;
}
if ($http_code == 200) {
try {
$data = json_decode($response, true);
$products = array_slice($data["results"], 0, 9); // Asumiendo que quieres los primeros 5 productos
return $products;
} catch (Exception $e) {
if ($e->getMessage() === 'Undefined index: results') { // Maneja solo el error de índice no definido
return [];
} else {
throw $e; // Propaga otros errores
}
}
} elseif (in_array($http_code, [400, 410, 429])) {
if ($http_code == 404) {
return [];
} else {
$key = array_search($dog_api, $api_keys);
if ($key !== false) {
unset($api_keys[$key]);
}
}
} else {
$retries++;
}
}
return [];
}
function get_amazon_domain_suffix() {
// Mapeo de países a sufijos de dominio de Amazon
$country_suffix_map = [
"USA" => "com",
"España" => "es",
"Japón" => "co.jp",
"Alemania" => "de",
"Australia" => "com.au",
"Brasil" => "com.br",
"Canadá" => "ca",
"Francia" => "fr",
"India" => "in",
"Italia" => "it",
"México" => "com.mx",
"Países Bajos" => "nl",
"Arabia Saudita" => "sa",
"Egipto" => "eg",
"Singapur" => "sg",
"Turquía" => "com.tr",
"Emiratos Árabes Unidos" => "ae",
"Reino Unido" => "co.uk",
"Polonia" => "pl",
"Suecia" => "se"
];
// Obtener el país seleccionado
$selected_country = get_option('tag_amazon_country'); // Reemplaza 'tag_amazon_country' con la opción correcta de WordPress
// Obtener el sufijo de dominio correspondiente al país seleccionado
$domain_suffix = $country_suffix_map[$selected_country] ?? "com"; // Por defecto, usa "com" si no se encuentra el país
return $domain_suffix;
}
function tag_make_api_request($data) {
// Obtener las API Keys
$api_keys = explode("\n", get_option('tag_api_keys'));
$api_provider = get_option('tag_api_provider', 'groq'); // 'groq' es el valor por defecto
// Array para almacenar las API Keys temporalmente no disponibles y su tiempo de espera
$temp_unavailable_keys = get_option('temp_unavailable_keys_' . $api_provider, array());
// Eliminar de $api_keys las que están temporalmente no disponibles
foreach ($temp_unavailable_keys as $key => $timeout) {
if (time() < $timeout) {
unset($api_keys[$key]);
} else {
// Si el tiempo de espera ha expirado, eliminar la key del array de no disponibles
unset($temp_unavailable_keys[$key]);
}
}
// Guardar el array actualizado de keys no disponibles
update_option('temp_unavailable_keys_' . $api_provider, $temp_unavailable_keys);
// Verificar si quedan API keys disponibles
if (empty($api_keys)) {
return new WP_Error('no_api_keys', 'No hay API Keys disponibles para este proveedor.');
}
// Seleccionar una API Key al azar
$api_key = $api_keys[array_rand($api_keys)];
// Definir la URL de la API según el proveedor
switch ($api_provider) {
case 'groq':
$url = 'https://api.groq.com/openai/v1/chat/completions';
break;
case 'together':
$url = 'https://api.together.xyz/v1/chat/completions';
break;
case 'hyperbolic':
$url = 'https://api.hyperbolic.xyz/v1/chat/completions';
break;
case 'OpenAI':
$url = 'https://api.openai.com/v1/chat/completions';
break;
case 'SambanovaAI':
$url = 'https://api.sambanova.ai/v1/chat/completions';
break;
case 'FireworksAI':
$url = 'https://api.fireworks.ai/inference/v1/chat/completions';
break;
case 'Cohere':
$url = 'https://api.cohere.com/v2/chat';
break;
default:
return new WP_Error('invalid_api_provider', 'Proveedor de API inválido.');
}
$ch = curl_init($url);
curl_setopt_array($ch, array(
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => '',
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 300,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_POSTFIELDS => json_encode($data),
CURLOPT_HTTPHEADER => array(
'Content-Type: application/json',
'Authorization: Bearer ' . $api_key
),
));
$response = curl_exec($ch);
$err = curl_error($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
// Si hay un error de cURL, retornar el error
if ($err) {
return new WP_Error('cURL_error', $err);
}
// Comprobar el código de estado HTTP para todos los proveedores
if ($http_code !== 200) {
// Agregar la API Key al array de temporalmente no disponibles con un tiempo de espera de 20 segundos
$temp_unavailable_keys[$api_key] = time() + 20;
update_option('temp_unavailable_keys_' . $api_provider, $temp_unavailable_keys);
// Intentar con otra API Key (recursivo), incluyendo las que estén en espera
return tag_make_api_request($data);
}
// Decodificar la respuesta JSON
$decoded_response = json_decode($response);
// Verificar si la respuesta es válida
if (json_last_error() !== JSON_ERROR_NONE) {
return new WP_Error('json_decode_error', 'Error al decodificar la respuesta JSON.');
}
// Adaptar la respuesta de Cohere para simular la estructura de OpenAI
if ($api_provider === 'Cohere' && isset($decoded_response->message->content[0]->text)) {
$decoded_response->choices = [];
$decoded_response->choices[0] = new stdClass();
$decoded_response->choices[0]->message = new stdClass();
$decoded_response->choices[0]->message->content = $decoded_response->message->content[0]->text;
}
// Retornar la respuesta decodificada
return $decoded_response;
}
function tag_remove_underscores($text) {
$text = str_replace('_', ' ', $text);
// Eliminar espacios en blanco dobles
$text = preg_replace('/\s+/', ' ', $text);
return $text;
}
// Función para filtrar contenido no deseado.
function tag_filter_content($content) {
$unwanted_chars = [
'<!DOCTYPE html>', '<html lang="es">', '</head>', '</html>',
'<head>', '<meta charset="UTF-8">', '<meta name="viewport" content="width=device-width, initial-scale=1.0">',
'</title>', '<body>', '</body>', 'style="color:4caf50;"', '"','可以','道路','لديه','استفاده گاز','壹','모든','다양한','망을','訩'
];
$ilimitado_chars = [
'<html lang="ja">', '<style>', '</style>', 'h2, h3 {font-family: "Arial", sans-serif;}', '<section>', '</section>',
'Preguntas Frecuentes sobre', '```html', '```', 'Hola,', 'Buenas tardes,', 'Respuesta:',
'Como periodista profesional que habla y escribe solo español,', 'Hola. Estoy aquí para responder tu pregunta.','ظم','دنی','ثبت ترین','دنی','아니라','수의','クロ'
];
$ilimitado23_chars = [
'Subjudul H3:', '#', 'Respuesta:', 'Buenas tardes,', 'Hola,', 'Gracias por su pregunta.',
'Espero que esta información te sea útil', 'Me alegra poder asistirte en español.',
'Hola. Gracias por tu pregunta.', 'Subtítulos H3 Relacionados:', 'Subtítulos H3:',
'Me complace responderte en español.', 'Conclusión:', 'Conclusión', 'Hola. Estoy aquí para responder tu pregunta.',
'Subtítulo H2:'
];
$extra_chars = [
'Subtítulo H2', 'Titulo H3', 'H3:', 'introducción', '#', '*', 'H2', 'Title:',
'Introduction:', 'Introducción:', '<html lang="it">', '<title>', '<h3>Preguntas frecuentes:</h3>',
'HTML:', '回答:', 'Respuesta:',':::::::::::','ㆅ'
];
foreach (array_merge($unwanted_chars, $ilimitado_chars, $ilimitado23_chars, $extra_chars) as $char) {
$content = str_replace($char, '', $content);
}
return $content;
}
// Función para verificar si es nicho directorio
function is_reseñas_individuales_niche() {
$is_resenas = get_option('tag_niche_type');
return $is_resenas === 'si'; // Ajusta 'si' según el valor que indica que es reseñas individuales
}
function tag_process_places_queue() {
// Verificar si la configuración es de nicho de reseñas individuales
if (!is_reseñas_individuales_niche()) {
return; // Detener la función si no es nicho de reseñas individuales
}
$queue = get_option('tag_article_queue', array());
$failed_titles = get_option('tag_failed_titles', array());
$created_titles = get_option('tag_created_titles', array());
$Tematica = get_option('tag_tematica_directorio', '');
$categories = explode('|', sanitize_text_field(get_option('tag_categories')));
$faqs_title = get_option('tag_faqs_title', 'Preguntas Frecuentes');
// Obtener el idioma seleccionado
$language = get_option('tag_language', 'Español');
// Ajustar el idioma para las variantes del español
$spanishVariants = array(
'Español (Perú)', 'Español (México)', 'Español (Argentina)', 'Español (Chile)',
'Español (Colombia)', 'Español (Ecuador)', 'Español (Venezuela)', 'Español (Guatemala)',
'Español (Cuba)', 'Español (Bolivia)', 'Español (Haití)', 'Español (República Dominicana)',
'Español (Honduras)', 'Español (Paraguay)', 'Español (Nicaragua)', 'Español (El Salvador)',
'Español (Costa Rica)', 'Español (Puerto Rico)', 'Español (Panamá)', 'Español (Uruguay)',
'Español (Jamaica)', 'Español (Trinidad y Tobago)', 'Español (Belice)'
);
if (in_array($language, $spanishVariants)) {
$language = 'Español';
}
// Obtener el modelo seleccionado
$model = get_option('tag_model', 'meta-llama/Llama-3-70b-chat-hf');
if (!empty($queue)) {
$titulo = array_shift($queue); // Título principal de la cola
// --- Obtener datos de los lugares desde Google Maps (Serper) UNA SOLA VEZ ---
$places = tag_call_google_maps_dev($titulo);
if (!empty($places)) {
$max_posts_per_run = 10; // Define la cantidad máxima de posts a crear por ejecución
$posts_created = 0; // Contador de posts creados en esta ejecución
foreach ($places as $place) {
// --- Verificar si ya existe un post en WordPress con el mismo título ---
$existing_post = get_page_by_title($place['title'], OBJECT, 'post');
if ($existing_post) {
error_log("Ya existe un post con el título: '" . $place['title'] . "'");
continue; // Saltar al siguiente lugar si ya existe un post
}
// --- Verificar si ya existe un post en WordPress con el mismo título ---
$sanitized_title = sanitize_title(trim($place['title']));
$existing_post = get_page_by_path($sanitized_title, OBJECT, 'post');
if ($existing_post) {
error_log("Ya existe un post con el título sanitizado: '" . $sanitized_title . "'");
continue; // Saltar al siguiente lugar si ya existe un post
}
// --- Verificar si el título ya ha sido procesado (está en la lista de creados) ---
if (in_array(trim($place['title']), $created_titles)) {
error_log("El título '" . $place['title'] . "' ya ha sido procesado.");
continue; // Saltar al siguiente lugar si ya se ha procesado
}
// --- Limitar la cantidad de posts creados ---
if ($posts_created >= $max_posts_per_run) {
error_log("Se ha alcanzado el límite de posts por ejecución ($max_posts_per_run).");
break; // Salir del bucle foreach si se ha alcanzado el límite
}
// Seleccionar una categoría aleatoria correctamente
$randomCategoryIndex = array_rand($categories);
$selectedCategory = $categories[$randomCategoryIndex];
// Generar la introducción para el lugar
$intro_place_prompt = "Crea una introducción de 90 palabras para un artículo sobre: '" . $place['title'] . "', que es de la ciudad de $titulo, en idioma $language. a base de la tematica:'$Tematica'. No incluyas ningún otro texto ante de la introducción. No hagas ninguna traducción innecesaria, no hables ningún otro idioma que no fuera solo $language.";
$data_intro_place = array(
'model' => $model,
'messages' => array(
array('role' => 'system', 'content' => 'Eres un redactor nativo en ' . $language . ' y escribes de manera neutral solo en ' . $language . '.'),
array('role' => 'user', 'content' => $intro_place_prompt)
),
'temperature' => 0.75,
'max_tokens' => 250,
);
$response_intro_place = tag_make_api_request($data_intro_place);
if (is_wp_error($response_intro_place)) {
error_log("Error en la generación de la introducción del lugar: " . $response_intro_place->get_error_message());
$failed_titles[] = trim($place['title']);
update_option('tag_failed_titles', $failed_titles);
continue;
}
$intro = tag_filter_content($response_intro_place->choices[0]->message->content);
$intro = str_replace('"', '', $intro);
// Generar la reseña para el lugar
$review_place_prompt = "Por favor, no me saludes, Solo Crea una reseña para: '" . $place['title'] . "' a base de la tematica: '$Tematica', puedes usar estos datos para la reseña: " . $place['address'] . ", " . $place['phoneNumber'] . ", " . $place['website'] . " y " . $place['rating'] . " en $language. No hables ningún otro idioma que no fuera solo $language.";
$data_review_place = array(
'model' => $model,
'messages' => array(
array('role' => 'system', 'content' => 'Eres un redactor nativo en ' . $language . ' y escribes de manera neutral solo en ' . $language . '.'),
array('role' => 'user', 'content' => $review_place_prompt)
),
'temperature' => 0.75,
'max_tokens' => 2000,
);
$response_review_place = tag_make_api_request($data_review_place);
if (is_wp_error($response_review_place)) {
error_log("Error en la generación de la reseña del lugar: " . $response_review_place->get_error_message());
$failed_titles[] = trim($place['title']);
update_option('tag_failed_titles', $failed_titles);
continue;
}
$response = tag_filter_content($response_review_place->choices[0]->message->content);
$response = preg_replace('/\*\*(.*?)\*\*/', '<strong>$1</strong>', $response);
// Generar las preguntas frecuentes para el lugar
$faqs_place_prompt = "Crea 4 Preguntas Frecuentes en $language, Acerca de: '" . $place['title'] . "' a base de la tematica: '$Tematica'. Formatea cada pregunta como subtítulo H3 en HTML '<h3></h3>' y responde inmediatamente después de cada pregunta con párrafos extensos en $language, utilizando negritas en HTML '<strong></strong>' para las palabras más relevantes. puedes usar estos datos: " . $place['address'] . ", " . $place['phoneNumber'] . ", " . $place['website'] . " y " . $place['rating'] . " en $language. No saludes antes de las preguntas, no hables ningún otro idioma que no fuera solo $language.";
$data_faqs_place = array(
'model' => $model,
'messages' => array(
array('role' => 'system', 'content' => 'Eres un redactor nativo en ' . $language . ' y escribes de manera neutral solo en ' . $language . '.'),
array('role' => 'user', 'content' => $faqs_place_prompt)
),
'temperature' => 0.75,
'max_tokens' => 2000,
);
$response_faqs_place = tag_make_api_request($data_faqs_place);
if (is_wp_error($response_faqs_place)) {
error_log("Error en la generación de las preguntas frecuentes del lugar: " . $response_faqs_place->get_error_message());
$failed_titles[] = trim($place['title']);
update_option('tag_failed_titles', $failed_titles);
continue;
}
$faqs = tag_filter_content($response_faqs_place->choices[0]->message->content);
$faqs = preg_replace('/\*\*(.*?)\*\*/', '<strong>$1</strong>', $faqs);
$imagen_link = tag_call_google_imagen_directorio_dev($place['title']);
$shortcode_de_resena = "[hostal_shortcode titulo=\"" . $place['title'] . "\" direccion=\"" . $place['address'] . "\" imagen_url=\"" . $imagen_link . "\" telefono=\"" . $place['phoneNumber'] . "\" como_llegar_url=\"" . $place['website'] . "\" rating=\"" . $place['rating'] . "/5\"]";
// Verificar si el contenido tiene datos antes de crear el post
if (!empty($intro) && !empty($response) && !empty($faqs)) {
// Crear el post en WordPress
$post_data = array(
'post_title' => sanitize_text_field($place['title']),
'post_content' => $intro . "\n\n" . $shortcode_de_resena . "\n\n" . $response . "\n\n<h2>" . sanitize_text_field($faqs_title) . "</h2>" . $faqs,
'post_status' => sanitize_text_field(get_option('tag_post_status', 'draft')),
'post_category' => array(get_cat_ID($selectedCategory))
);
$post_id = wp_insert_post($post_data);
if (!is_wp_error($post_id)) {
error_log("Post creado exitosamente con el título: '" . $place['title'] . "'");
$created_titles[] = trim($place['title']);
// Incrementar el contador de posts creados, pero solo si es menor que el límite
if ($posts_created < $max_posts_per_run) {
$posts_created++;
}
} else {
error_log("Error al crear el post para el título: '" . $place['title'] . "'");
$failed_titles[] = trim($place['title']);
}
update_option('tag_created_titles', $created_titles);
update_option('tag_failed_titles', $failed_titles);
} else {
// Manejar caso de contenido vacío
error_log("No se puede crear el post, el contenido está vacío para el título: " . esc_html($place['title']));
$failed_titles[] = trim($place['title']);
update_option('tag_failed_titles', $failed_titles);
$serper_consulted_titles = get_option('tag_serper_consulted_titles', array());
if (($key = array_search($title, $serper_consulted_titles)) !== false) {
unset($serper_consulted_titles[$key]);
}
update_option('tag_serper_consulted_titles', $serper_consulted_titles);
}
}
// Actualizar la cola eliminando el título principal si se han creado todos los posts posibles
if ($posts_created >= $max_posts_per_run) {
error_log("Se completó la creación de posts para el título: '" . $titulo . "'");
} else {
error_log("Se procesaron todos los lugares posibles para el título: '" . $titulo . "'");
}
}
update_option('tag_article_queue', $queue); // Guardar la cola actualizada
}
}
// Función para verificar si es nicho directorio
function is_directorio_niche() {
$is_directorio = get_option('tag_nicho_directorio');
return $is_directorio === 'si'; // Ajusta 'si' según el valor que indica que es nicho directorio
}
function tag_process_directorio_queue() {
// Verificar si la configuración es de nicho de directorio
if (!is_directorio_niche()) {
return; // Detener la función si no es nicho de directorio
}
// --- Verificar si las API Keys de Serper están configuradas ---
$serper_api_keys = get_option('tag_serper_api_keys');
if (empty($serper_api_keys)) {
// Manejar el caso en que las API Keys de Serper no están configuradas
error_log("Error: Las API Keys de Serper son obligatorias para el nicho de directorio.");
// Opcional: puedes mostrar un mensaje de error en la interfaz del plugin aquí
return; // Detener la ejecución de la función
}
// -------------------------------------------------------------
$queue = get_option('tag_article_queue', array());
$failed_titles = get_option('tag_failed_titles', array());
// Array para almacenar los títulos consultados a Serper
$serper_consulted_titles = get_option('tag_serper_consulted_titles', array());
if (!empty($queue)) {
$title = array_shift($queue);
$created_titles = get_option('tag_created_titles', array());
$delay = intval(get_option('tag_article_delay'));
$categories = explode('|', sanitize_text_field(get_option('tag_categories')));
$Tematica = get_option('tag_tematica_directorio', ''); // Obtener la temática, valor por defecto vacío
// --- Verificar si ya existe un post con el mismo título ---
$existing_post = get_page_by_title( $title, OBJECT, 'post' );
if ( $existing_post ) {
error_log("Ya existe un post con el título: '$title'");
update_option('tag_article_queue', $queue); // Actualizar la cola sin procesar este título
return;
}
// --- Verificar si el título ya ha sido consultado a Serper ---
if (in_array($title, $serper_consulted_titles)) {
error_log("El título '$title' ya ha sido consultado a Serper.");
update_option('tag_article_queue', $queue); // Actualizar la cola sin procesar este título
return;
}
// -------------------------------------------------------
// Obtener el idioma seleccionado
$language = get_option('tag_language', 'Español');
// Ajustar el idioma para las variantes del español
$spanishVariants = array(
'Español (Perú)', 'Español (México)', 'Español (Argentina)', 'Español (Chile)',
'Español (Colombia)', 'Español (Ecuador)', 'Español (Venezuela)', 'Español (Guatemala)',
'Español (Cuba)', 'Español (Bolivia)', 'Español (Haití)', 'Español (República Dominicana)',
'Español (Honduras)', 'Español (Paraguay)', 'Español (Nicaragua)', 'Español (El Salvador)',
'Español (Costa Rica)', 'Español (Puerto Rico)', 'Español (Panamá)', 'Español (Uruguay)',
'Español (Jamaica)', 'Español (Trinidad y Tobago)', 'Español (Belice)'
);
if (in_array($language, $spanishVariants)) {
$language = 'Español';
}
// Obtener el modelo seleccionado
$model = get_option('tag_model', 'meta-llama/Llama-3-70b-chat-hf');
// Definir los prompts.
$intro_prompt = get_option('tag_intro_prompt', "Crea una introducción de 100 palabras, en idioma $language, para un artículo sobre: '$title'. No incluyas ningún otro texto antes de la introducción. No saludes, ni respondas nada más, ni hagas ninguna traducción innecesaria. No hables ningún otro idioma que no fuera solo $language.");
$faqs_prompt = get_option('tag_faqs_prompt', "Solo Crea unicamente 4 Preguntas Frecuentes en $language, acerca de '$title'. Formatea cada pregunta como subtítulo H3 en HTML '<h3></h3>' y responde inmediatamente después de cada pregunta con un párrafo extenso en $language, utilizando negritas en HTML '<strong></strong>' para las palabras más relevantes. No incluyas ningún otro texto antes de las preguntas. No saludes antes de las preguntas, no hables ningún otro idioma que no fuera solo $language.");
$system_prompt = get_option('tag_system_prompt', "Eres un redactor nativo en ' . $language . ' y escribes de manera neutral solo en ' . $language . '. ");
$intro_prompt = str_replace(['$title', '$language'], [$title, $language], $intro_prompt);
$faqs_prompt = str_replace(['$title', '$language'], [$title, $language], $faqs_prompt);
$system_prompt = str_replace('$language', $language, $system_prompt);
// --- Llamar a las funciones de Serper solo si hay API keys configuradas ---
if (!empty($serper_api_keys)) {
$youtube_video_url = tag_call_google_serp_dev_videos($title);
$places_data = tag_call_google_maps_dev($title); // Obtener datos de lugares
// --- Agregar el título a la lista de títulos consultados a Serper ---
$serper_consulted_titles[] = $title;
update_option('tag_serper_consulted_titles', $serper_consulted_titles);
} else {
$youtube_video_url = null;
$places_data = null;
}
// ----------------------------------------------------------------------
// Datos para la llamada a la API.
$data_intro = array(
'model' => $model,
'messages' => array(
array('role' => 'system', 'content' => $system_prompt),
array('role' => 'user', 'content' => $intro_prompt)
),
'temperature' => 0.90,
'max_tokens' => 500,
);
// Datos para la llamada a la API.
$prompt_h2_2 = array(
'model' => $model,
'messages' => array(
array('role' => 'system', 'content' => $system_prompt),
array('role' => 'user', 'content' => 'Crea 1 subtítulo H2 que aborde sobre la tematica: ' . $title . ' en ' . $language . '. Coloca ese subtítulo en HTML "<h2></h2>". no hables ningun otro idioma que no fuera solo ' . $language . '')
),
'temperature' => 0.90,
'max_tokens' => 500,
);
$data_faqs = array(
'model' => $model,
'messages' => array(
array('role' => 'system', 'content' => $system_prompt),
array('role' => 'user', 'content' => $faqs_prompt)
),
'temperature' => 0.90,
'max_tokens' => 2000,
);
// Realizar solicitudes a la API para intro y faqs.
$response_intro = tag_make_api_request($data_intro);
$response_faqs = tag_make_api_request($data_faqs);
$prompt_h2_2 = tag_make_api_request($prompt_h2_2);
// Verificar si alguna de las respuestas es un error o está vacía
if (is_wp_error($response_intro) || is_wp_error($response_faqs) || is_wp_error($prompt_h2_2) ||
empty($response_intro) || empty($response_faqs) || empty($prompt_h2_2)) {
// Obtener la lista actual de títulos fallidos
$failed_titles = get_option('tag_failed_titles', array());
// Agregar el título fallido a la lista
$failed_titles[] = $title; // Usar la variable $title que contiene el título fallido
// Actualizar la opción con la nueva lista de títulos fallidos
update_option('tag_failed_titles', array_unique($failed_titles));
// Actualizar la cola de artículos
$queue = get_option('tag_article_queue', array());
// Eliminar el título fallido de la cola
if (($key = array_search($title, $queue)) !== false) {
unset($queue[$key]);
}
// Guardar la cola actualizada
update_option('tag_article_queue', $queue);
// Detener el proceso para evitar más operaciones con este título fallido
return;
}
$intro = tag_filter_content($response_intro->choices[0]->message->content);
$intro = str_replace($title, '', $intro);
$prompt_h2_2 = tag_filter_content($prompt_h2_2->choices[0]->message->content);
$faqs = tag_filter_content($response_faqs->choices[0]->message->content);
$faqs = tag_remove_underscores($faqs);
// Obtener el título del H2 de Preguntas Frecuentes
$faqs_title = get_option('tag_faqs_title', 'Preguntas Frecuentes');
// Seleccionar una categoría aleatoria correctamente
$randomCategoryIndex = array_rand($categories);
$selectedCategory = $categories[$randomCategoryIndex];
// --- Generar contenido para nicho directorio ---
if (!empty($places_data)) {
$datos_places = "";
foreach ($places_data as $place) {
// Generar la reseña para el lugar (para el post principal)
$review_place_prompt = "Crea una reseña corta para: '" . $place['title'] . "' a base de la tematica:' . $Tematica . '. Puedes usar estos datos para la reseña: " . $place['address'] . ", " . $place['phoneNumber'] . ", " . $place['website'] . " y " . $place['rating'] . ". No hables ningun otro idioma que no fuera solo $language. Máximo 200 palabras.";
$data_review_place = array(
'model' => $model,
'messages' => array(
array('role' => 'system', 'content' => $system_prompt),
array('role' => 'user', 'content' => $review_place_prompt)
),
'temperature' => 0.75,
'max_tokens' => 1000,
);
$response_review_place = tag_make_api_request($data_review_place);
if (is_wp_error($response_review_place)) {
// Manejar errores de la API
error_log("Error en la generación de la reseña del lugar: " . $response_review_place->get_error_message());
continue;
}
$review_place = tag_filter_content($response_review_place->choices[0]->message->content);
$review_place = preg_replace('/\*\*(.*?)\*\*/', '<strong>$1</strong>', $review_place);
// Obtener la imagen para el lugar
$imagen_link = tag_call_google_imagen_directorio_dev($place['title']);
// Crear el shortcode para el lugar
$shortcode_place = "[hostal_shortcode titulo=\"" . $place['title'] . "\" direccion=\"" . $place['address'] . "\" imagen_url=\"" . $imagen_link . "\" telefono=\"" . $place['phoneNumber'] . "\" como_llegar_url=\"" . $place['website'] . "\" rating=\"" . $place['rating'] . "/5\"]";
// Agregar la información al contenido del post principal
$datos_places .= "<h3>" . $place['title'] . "</h3>\n";
$datos_places .= "$shortcode_place\n";
$datos_places .= $review_place . "\n";
}
// Verificar que el contenido no esté vacío antes de crear el post
if (!empty($intro) && !empty($prompt_h2_2) && !empty($datos_places) && !empty($faqs)) {
// Crear el post principal del directorio
$post_data = array(
'post_title' => sanitize_text_field($title),
'post_content' => wp_kses_post("$intro\n\n$youtube_video_url\n\n$prompt_h2_2\n\n$datos_places\n\n<h2>$faqs_title</h2>\n$faqs"),
'post_status' => get_option('tag_post_status', 'publish'),
'post_author' => 1,
'post_category' => array(get_cat_ID($selectedCategory))
);
$post_id = wp_insert_post($post_data);
if (is_wp_error($post_id)) {
// Manejar errores al crear el post
error_log("Error al crear el post principal del directorio: " . $post_id->get_error_message());
// Agregar el título a la lista de fallidos
$failed_titles[] = trim($title);
update_option('tag_failed_titles', array_unique($failed_titles));
$serper_consulted_titles = get_option('tag_serper_consulted_titles', array());
if (($key = array_search($title, $serper_consulted_titles)) !== false) {
unset($serper_consulted_titles[$key]);
}
update_option('tag_serper_consulted_titles', $serper_consulted_titles);
} else {
// Agregar título a la lista de títulos creados.
$created_titles[] = trim($title);
update_option('tag_created_titles', array_unique($created_titles));
// Registrar el éxito en los logs (opcional)
error_log("Post creado con éxito: " . get_edit_post_link($post_id) . " - " . esc_html($title));
}
} else {
// Manejar caso de contenido vacío
error_log("No se puede crear el post principal del directorio, el contenido está vacío para el título: " . esc_html($title));
// Agregar el título a la lista de fallidos
$failed_titles[] = trim($title);
update_option('tag_failed_titles', array_unique($failed_titles));
$serper_consulted_titles = get_option('tag_serper_consulted_titles', array());
if (($key = array_search($title, $serper_consulted_titles)) !== false) {
unset($serper_consulted_titles[$key]);
}
update_option('tag_serper_consulted_titles', $serper_consulted_titles);
}
// Actualizar la cola
update_option('tag_article_queue', $queue);
}
}
}
function tag_process_article_queue() {
// Verificar si la configuración NO es de nicho de directorio NI de reseñas individuales NI DE AMAZON SIN API
if (is_directorio_niche() || is_reseñas_individuales_niche() || is_amazon_sin_api_niche()) {
return;
}
// Array para almacenar los títulos consultados a Serper
$serper_consulted_titles = get_option('tag_serper_consulted_titles', array());
$queue = get_option('tag_article_queue', array());
$failed_titles = get_option('tag_failed_titles', array());
if (!empty($queue)) {
$title = array_shift($queue);
$created_titles = get_option('tag_created_titles', array());
$delay = intval(get_option('tag_article_delay'));
$categories = explode('|', sanitize_text_field(get_option('tag_categories')));
// --- Verificar si ya existe un post con el mismo título ---
$args = array(
'post_type' => 'post', // Especificar el tipo de post como 'post'
'title' => $title, // Buscar por el título
'posts_per_page' => 1 // Limitar la búsqueda a un solo post
);
$existing_post = get_posts($args);
if (!empty($existing_post)) {
error_log("Ya existe un post con el título: '$title'");
update_option('tag_article_queue', $queue); // Actualizar la cola sin procesar este título
return;
}
// --- Verificar si el título ya ha sido consultado a Serper ---
if (in_array($title, $serper_consulted_titles)) {
error_log("El título '$title' ya ha sido consultado a Serper.");
update_option('tag_article_queue', $queue); // Actualizar la cola sin procesar este título
return;
}
// Obtener el idioma seleccionado
$language = get_option('tag_language', 'Español');
// Ajustar el idioma para las variantes del español
$spanishVariants = array(
'Español (Perú)', 'Español (México)', 'Español (Argentina)', 'Español (Chile)',
'Español (Colombia)', 'Español (Ecuador)', 'Español (Venezuela)', 'Español (Guatemala)',
'Español (Cuba)', 'Español (Bolivia)', 'Español (Haití)', 'Español (República Dominicana)',
'Español (Honduras)', 'Español (Paraguay)', 'Español (Nicaragua)', 'Español (El Salvador)',
'Español (Costa Rica)', 'Español (Puerto Rico)', 'Español (Panamá)', 'Español (Uruguay)',
'Español (Jamaica)', 'Español (Trinidad y Tobago)', 'Español (Belice)'
);
if (in_array($language, $spanishVariants)) {
$language = 'Español';
}
// Obtener el modelo seleccionado
$model = get_option('tag_model', 'meta-llama/Llama-3-70b-chat-hf');
// Definir los prompts.
$intro_prompt = get_option('tag_intro_prompt', "Crea una introducción de 100 palabras, en idioma $language, para un artículo sobre: '$title'. No incluyas ningún otro texto antes de la introducción. No saludes, ni respondas nada más, ni hagas ninguna traducción innecesaria. No hables ningún otro idioma que no fuera solo $language.");
$subtitles_prompt = get_option('tag_subtitles_prompt', "Crea 1 subtítulo H2 relacionado a la temática: '$title' en $language. Coloca ese subtítulo en HTML '<h2></h2>' y luego responde de forma detallada en $language y agrega también 5 Subtítulos H3 informacionales en $language sobre '$title' en HTML '<h3></h3>' y responde cada Subtítulo h3 de forma detallada y añade una tabla con el formato: '<table></table>' y pon en negrita en HTML '<strong></strong>' las palabras más relevantes en $language y no vayas a añadir una conclusión final, no hables ningún otro idioma que no fuera solo $language.");
$faqs_prompt = get_option('tag_faqs_prompt', "Solo Crea unicamente 4 Preguntas Frecuentes en $language, acerca de '$title'. Formatea cada pregunta como subtítulo H3 en HTML '<h3></h3>' y responde inmediatamente después de cada pregunta con un párrafo extenso en $language, utilizando negritas en HTML '<strong></strong>' para las palabras más relevantes. No incluyas ningún otro texto antes de las preguntas. No saludes antes de las preguntas, no hables ningún otro idioma que no fuera solo $language.");
$system_prompt = get_option('tag_system_prompt', "Eres un redactor nativo en ' . $language . ' y escribes de manera neutral solo en ' . $language . '. ");
$intro_prompt = str_replace(['$title', '$language'], [$title, $language], $intro_prompt);
$subtitles_prompt = str_replace(['$title', '$language'], [$title, $language], $subtitles_prompt);
$faqs_prompt = str_replace(['$title', '$language'], [$title, $language], $faqs_prompt);
$system_prompt = str_replace('$language', $language, $system_prompt); // Reemplazar $language en $system_prompt
// --- Llamar a las funciones de Serper solo si hay API keys configuradas ---
$serper_api_keys = get_option('tag_serper_api_keys');
if (!empty($serper_api_keys)) {
$related_questions = tag_call_google_serp_dev($title);
$youtube_video_url = tag_call_google_serp_dev_videos($title);
// --- Agregar el título a la lista de títulos consultados a Serper ---
$serper_consulted_titles[] = $title;
update_option('tag_serper_consulted_titles', $serper_consulted_titles);
} else {
$related_questions = null;
$youtube_video_url = null;
}
// ----------------------------------------------------------------------
// Datos para la llamada a la API.
$data_intro = array(
'model' => $model,
'messages' => array(
array('role' => 'system', 'content' => $system_prompt),
array('role' => 'user', 'content' => $intro_prompt)
),
'temperature' => 0.90,
'max_tokens' => 500,
);
// Datos para la llamada a la API para determinar la categoría.
$data_categoria = array(
'model' => $model,
'messages' => array(
array('role' => 'system', 'content' => $system_prompt),
array('role' => 'user', 'content' => "Determina la categoría más adecuada para el artículo: '$title'. Las categorías posibles son: " . implode('|', $categories) . ". No crees ni respondas, solo elige una de estas: " . implode('|', $categories))
),
'temperature' => 0.75,
'max_tokens' => 2000,
);
$data_subtitles = array(
'model' => $model,
'messages' => array(
array('role' => 'system', 'content' => $system_prompt),
array('role' => 'user', 'content' => $subtitles_prompt)
),
'temperature' => 0.90,
'max_tokens' => 2500,
);
$data_faqs = array(
'model' => $model,
'messages' => array(
array('role' => 'system', 'content' => $system_prompt),
array('role' => 'user', 'content' => $faqs_prompt)
),
'temperature' => 0.90,
'max_tokens' => 2000,
);
// Realizar solicitudes a la API.
$response_intro = tag_make_api_request($data_intro);
$response_subtitles = tag_make_api_request($data_subtitles);
$response_faqs = tag_make_api_request($data_faqs);
$response_categoria = tag_make_api_request($data_categoria);
// Verificar si alguna de las respuestas es un error o está vacía
if (is_wp_error($response_intro) || is_wp_error($response_faqs) || is_wp_error($response_categoria) || is_wp_error($response_subtitles) ||
empty($response_intro) || empty($response_faqs) || empty($response_categoria) || empty($response_subtitles)) {
// Obtener la lista actual de títulos fallidos
$failed_titles = get_option('tag_failed_titles', array());
// Agregar el título fallido a la lista
$failed_titles[] = $title; // Usar la variable $title que contiene el título fallido
// Actualizar la opción con la nueva lista de títulos fallidos
update_option('tag_failed_titles', array_unique($failed_titles));
// Actualizar la cola de artículos
$queue = get_option('tag_article_queue', array());
// Eliminar el título fallido de la cola
if (($key = array_search($title, $queue)) !== false) {
unset($queue[$key]);
}
// Guardar la cola actualizada
update_option('tag_article_queue', $queue);
// Detener el proceso para evitar más operaciones con este título fallido
return;
}
$intro = tag_filter_content($response_intro->choices[0]->message->content);
$intro = str_replace($title, '', $intro);
$subtitles = tag_filter_content($response_subtitles->choices[0]->message->content);
$faqs = tag_filter_content($response_faqs->choices[0]->message->content);
$categoria_elegida = tag_filter_content($response_categoria->choices[0]->message->content);
// Quitar caracteres "_" y reemplazarlos con un espacio
$subtitles = tag_remove_underscores($subtitles);
$faqs = tag_remove_underscores($faqs);
// Obtener el título del H2 de Preguntas Frecuentes
$faqs_title = get_option('tag_faqs_title', 'Preguntas Frecuentes');
// Obtener la ID de la categoría elegida por la IA
$selected_category_id = get_cat_ID($categoria_elegida);
// Si la categoría no existe, usar una categoría aleatoria
if (!$selected_category_id) {
$randomCategoryIndex = array_rand($categories);
$selectedCategory = $categories[$randomCategoryIndex];
$selected_category_id = get_cat_ID($selectedCategory);
}
// --- Generar respuestas para las preguntas de Serper con Together (solo si hay preguntas) ---
$questions_and_answers = "";
if ($related_questions) {
foreach ($related_questions as $question) {
// Generar la respuesta con Together
$question_prompt = get_option('tag_question_prompt', "Por favor, no me saludes, Solo Responde Detalladamente en: '{$language}', la siguiente pregunta: {$question}. Y agrega también 3 Subtítulos H3 Relacionados en '{$language}' sobre {$question} en HTML '<h3></h3>' y responde cada Subtítulo h3 de forma detallada con un párrafo y un formato lista para enriquecer el texto: '<ol><li></li><li></li><li></li></ol>', en '{$language}', y pon en negrita en HTML '<strong></strong>' las palabras más relevantes. No saludes al finalizar, NO HAGAS NINGUNA CONCLUSION, no hables ningún otro idioma que no fuera solo '{$language}'.");
$question_prompt = str_replace(['$question', '$language'], [$question, $language], $question_prompt);
$data_question = array(
'model' => $model,
'messages' => array(
array('role' => 'system', 'content' => $system_prompt),
array('role' => 'user', 'content' => $question_prompt)
),
'temperature' => 0.90, // Puedes ajustar la temperatura
'max_tokens' => 2000, // Puedes ajustar el número máximo de tokens
);
$response_question = tag_make_api_request($data_question);
if (is_wp_error($response_question)) {
error_log("Error en la generación de la respuesta para la pregunta: " . $question . " - " . $response_question->get_error_message());
continue;
}
$answer = tag_filter_content($response_question->choices[0]->message->content);
$answer = str_replace($question, '', $answer);
// Eliminar todas las preguntas de $related_questions de la respuesta:
foreach ($related_questions as $q) {
$answer = str_replace($q, '', $answer);
}
// Obtener la imagen relacionada (solo si hay API keys configuradas)
if (!empty($serper_api_keys)) {
$image_url = tag_call_google_imagen_dev($question);
} else {
$image_url = null;
}
$image_html = $image_url ? "<p style='text-align: center;'><img src='" . esc_url($image_url) . "' style='display: block; margin: 0 auto;'></p>" : "";
// Combinar la pregunta, la imagen y la respuesta
$questions_and_answers .= "<h2>" . esc_html($question) . "</h2>" . $image_html . $answer;
}
}
// -------------------------------------------------------------------------------------------
// Crear el post en WordPress solo si el contenido no está vacío
if (!empty($intro) && !empty($faqs) && !empty($subtitles)) {
// Preparar los datos del post
$post_data = array(
'post_title' => sanitize_text_field($title),
'post_content' => wp_kses_post("$intro\n\n$youtube_video_url\n\n$subtitles\n\n$questions_and_answers\n\n<h2>$faqs_title</h2>\n$faqs"),
'post_status' => get_option('tag_post_status', 'publish'), // Obtener el estado del post
'post_author' => 1, // ID del autor. Cambiar según sea necesario.
'post_category' => array(get_cat_ID($categoria_elegida)) // Asignar la categoría por ID usando get_cat_ID
);
// Insertar el post
$post_id = wp_insert_post($post_data);
// Manejar el resultado de la inserción
if (is_wp_error($post_id)) {
// Manejar errores al crear el post
error_log("Error al crear el post: " . $post_id->get_error_message());
// Agregar el título a la lista de fallidos
$failed_titles[] = trim($title);
update_option('tag_failed_titles', array_unique($failed_titles));
$serper_consulted_titles = get_option('tag_serper_consulted_titles', array());
if (($key = array_search($title, $serper_consulted_titles)) !== false) {
unset($serper_consulted_titles[$key]);
}
update_option('tag_serper_consulted_titles', $serper_consulted_titles);
} else {
// Agregar el título a la lista de títulos creados
$created_titles[] = trim($title);
update_option('tag_created_titles', array_unique($created_titles));
// Registrar el éxito en los logs (opcional)
error_log("Post creado con éxito: " . get_edit_post_link($post_id) . " - " . esc_html($title));
}
} else {
// Manejar caso de contenido vacío
error_log("No se puede crear el post, el contenido está vacío para el título: " . esc_html($title));
// Agregar el título a la lista de fallidos
$failed_titles[] = trim($title);
update_option('tag_failed_titles', array_unique($failed_titles));
$serper_consulted_titles = get_option('tag_serper_consulted_titles', array());
if (($key = array_search($title, $serper_consulted_titles)) !== false) {
unset($serper_consulted_titles[$key]);
}
update_option('tag_serper_consulted_titles', $serper_consulted_titles);
}
// Actualizar la cola
update_option('tag_article_queue', $queue);
}
}
// Función para verificar si es nicho amazon sin api
function is_amazon_sin_api_niche() {
$is_amazon = get_option('tag_amazon_sin_api_niche');
return $is_amazon === 'si'; // Ajusta 'si' según el valor que indica que es nicho amazon sin api
}
function tag_process_amazon_sin_api_queue() {
// Verificar si la configuración es de nicho de amazon sin api
if (!is_amazon_sin_api_niche()) {
return; // Detener la función si no es nicho de amazon sin api
}
// Variable global para almacenar el último título de cola procesado
global $tag_last_processed_title;
// Array para almacenar los títulos consultados a Serper
$serper_consulted_titles = get_option('tag_serper_consulted_titles', array());
$queue = get_option('tag_article_queue', array());
$failed_titles = get_option('tag_failed_titles', array());
if (!empty($queue)) {
$title = array_shift($queue);
$created_titles = get_option('tag_created_titles', array());
$delay = intval(get_option('tag_article_delay'));
$categories = explode('|', sanitize_text_field(get_option('tag_categories')));
// --- Verificar si ya existe un post con el mismo título ---
$existing_post = get_page_by_title($title, OBJECT, 'post');
if ($existing_post) {
error_log("Ya existe un post con el título: '$title'");
update_option('tag_article_queue', $queue); // Actualizar la cola sin procesar este título
return;
}
// --- Verificar si el título ya ha sido consultado a Serper ---
if (in_array($title, $serper_consulted_titles)) {
error_log("El título '$title' ya ha sido consultado a Serper.");
update_option('tag_article_queue', $queue); // Actualizar la cola sin procesar este título
return;
}
$language = get_option('tag_language', 'Español');
$id_afiliado = get_option('tag_amazon_affiliate_id');
$domain_suffix = get_amazon_domain_suffix();
// Ajustar el idioma para las variantes del español
$spanishVariants = array(
'Español (Perú)', 'Español (México)', 'Español (Argentina)', 'Español (Chile)',
'Español (Colombia)', 'Español (Ecuador)', 'Español (Venezuela)', 'Español (Guatemala)',
'Español (Cuba)', 'Español (Bolivia)', 'Español (Haití)', 'Español (República Dominicana)',
'Español (Honduras)', 'Español (Paraguay)', 'Español (Nicaragua)', 'Español (El Salvador)',
'Español (Costa Rica)', 'Español (Puerto Rico)', 'Español (Panamá)', 'Español (Uruguay)',
'Español (Jamaica)', 'Español (Trinidad y Tobago)', 'Español (Belice)'
);
if (in_array($language, $spanishVariants)) {
$language = 'Español';
}
// Obtener el modelo seleccionado
$model = get_option('tag_model', 'meta-llama/Llama-3-70b-chat-hf');
// Definir los prompts.
$intro_prompt = get_option('tag_intro_prompt', "Crea una introducción de 100 palabras, en idioma $language, para un artículo sobre: '$title'. No incluyas ningún otro texto antes de la introducción. No saludes, ni respondas nada más, ni hagas ninguna traducción innecesaria. No hables ningún otro idioma que no fuera solo $language.");
$faqs_prompt = get_option('tag_faqs_prompt', "Solo Crea unicamente 4 Preguntas Frecuentes en $language, acerca de '$title'. Formatea cada pregunta como subtítulo H3 en HTML '<h3></h3>' y responde inmediatamente después de cada pregunta con un párrafo extenso en $language, utilizando negritas en HTML '<strong></strong>' para las palabras más relevantes. No incluyas ningún otro texto antes de las preguntas. No saludes antes de las preguntas, no hables ningún otro idioma que no fuera solo $language.");
$system_prompt = get_option('tag_system_prompt', "Eres un redactor nativo en ' . $language . ' y escribes de manera neutral solo en ' . $language . '. ");
$intro_prompt = str_replace(['$title', '$language'], [$title, $language], $intro_prompt);
$faqs_prompt = str_replace(['$title', '$language'], [$title, $language], $faqs_prompt);
$system_prompt = str_replace('$language', $language, $system_prompt); // Reemplazar $language en $system_prompt
// --- Llamar a las funciones de Serper solo si hay API keys configuradas ---
$serper_api_keys = get_option('tag_serper_api_keys');
if (!empty($serper_api_keys)) {
$related_questions = tag_call_google_serp_dev($title);
$youtube_video_url = tag_call_google_serp_dev_videos($title);
// --- Agregar el título a la lista de títulos consultados a Serper ---
$serper_consulted_titles[] = $title;
update_option('tag_serper_consulted_titles', $serper_consulted_titles);
} else {
$related_questions = null;
$youtube_video_url = null;
}
// ----------------------------------------------------------------------
// Datos para la llamada a la API.
$data_intro = array(
'model' => $model,
'messages' => array(
array('role' => 'system', 'content' => $system_prompt),
array('role' => 'user', 'content' => $intro_prompt)
),
'temperature' => 0.90,
'max_tokens' => 500,
);
// Datos para la llamada a la API para determinar la categoría.
$data_categoria = array(
'model' => $model,
'messages' => array(
array('role' => 'system', 'content' => $system_prompt),
array('role' => 'user', 'content' => "Determina la categoría más adecuada para el artículo: '$title'. Las categorías posibles son: " . implode('|', $categories) . ". No crees ni respondas, solo elige una de estas: " . implode('|', $categories))
),
'temperature' => 0.75,
'max_tokens' => 2000,
);
// Datos para la llamada a la API.
$prompt_h2_2 = array(
'model' => $model,
'messages' => array(
array('role' => 'system', 'content' => $system_prompt),
array('role' => 'user', 'content' => 'Crea 1 subtítulo H2 en ' . $language . ' que comience con una de las siguientes frases: "Los Mejores", "Top Reseñas", "Las Mejores Opciones", seguido del tema: ' . $title . '. Devuélvelo en formato HTML "<h2></h2>" y solo en ' . $language . '')
),
'temperature' => 0.90,
'max_tokens' => 500,
);
$data_faqs = array(
'model' => $model,
'messages' => array(
array('role' => 'system', 'content' => $system_prompt),
array('role' => 'user', 'content' => $faqs_prompt)
),
'temperature' => 0.90,
'max_tokens' => 2000,
);
// Realizar solicitudes a la API.
$response_intro = tag_make_api_request($data_intro);
$prompt_h2 = tag_make_api_request($prompt_h2_2);
$response_faqs = tag_make_api_request($data_faqs);
$response_categoria = tag_make_api_request($data_categoria);
// Generar el título SEO del POST con la IA
$titulo_seo_post_prompt = "Crea un título SEO optimizado no muy largo para un artículo sobre ' . $title . ' en ' . $language . '. NO SALUDES AL FINALIZAR. solo devuelve el titulo creado nada mas. no hables ningun otro idioma que no fuera solo ' . $language . '.";
$data_titulo_seo_post = [
'model' => $model,
'messages' => [
['role' => 'system', 'content' => $system_prompt],
['role' => 'user', 'content' => $titulo_seo_post_prompt]
],
'temperature' => 0.75,
'max_tokens' => 2000,
];
$response_titulo_seo_post = tag_make_api_request($data_titulo_seo_post);
if (is_wp_error($response_titulo_seo_post)) {
error_log("Error al generar el título SEO del post: " . $response_titulo_seo_post->get_error_message());
// Manejo de errores...
} else {
$titulo_seo_post = tag_filter_content($response_titulo_seo_post->choices[0]->message->content);
$titulo_seo_post = str_replace('"', '', $titulo_seo_post);
}
// Verificar si alguna de las respuestas es un error o está vacía
if (is_wp_error($response_intro) || is_wp_error($response_faqs) || is_wp_error($response_categoria) || is_wp_error($prompt_h2) || is_wp_error($response_titulo_seo_post) ||
empty($response_intro) || empty($response_faqs) || empty($response_categoria) || empty($prompt_h2) || empty($titulo_seo_post)) {
// Obtener la lista actual de títulos fallidos
$failed_titles = get_option('tag_failed_titles', array());
// Agregar el título fallido a la lista
$failed_titles[] = $title; // Usar la variable $title que contiene el título fallido
// Actualizar la opción con la nueva lista de títulos fallidos
update_option('tag_failed_titles', array_unique($failed_titles));
// Actualizar la cola de artículos
$queue = get_option('tag_article_queue', array());
// Eliminar el título fallido de la cola
if (($key = array_search($title, $queue)) !== false) {
unset($queue[$key]);
}
// Guardar la cola actualizada
update_option('tag_article_queue', $queue);
// Detener el proceso para evitar más operaciones con este título fallido
return;
}
$intro = tag_filter_content($response_intro->choices[0]->message->content);
$intro = str_replace($title, '', $intro);
$prompt_h2 = tag_filter_content($prompt_h2->choices[0]->message->content);
$prompt_h2_arreglado = preg_replace('/\b(2021|2022|2023)\b/', '2024', $prompt_h2);
$faqs = tag_filter_content($response_faqs->choices[0]->message->content);
$faqs = tag_remove_underscores($faqs);
$categoria_elegida = tag_filter_content($response_categoria->choices[0]->message->content);
// Obtener el título del H2 de Preguntas Frecuentes
$faqs_title = get_option('tag_faqs_title', 'Preguntas Frecuentes');
// Obtener la ID de la categoría elegida por la IA
$selected_category_id = get_cat_ID($categoria_elegida);
// Si la categoría no existe, usar una categoría aleatoria
if (!$selected_category_id) {
$randomCategoryIndex = array_rand($categories);
$selectedCategory = $categories[$randomCategoryIndex];
$selected_category_id = get_cat_ID($selectedCategory);
}
// --- Buscar productos de Amazon ---
if ($language === 'Japonés' || $language === 'Árabe' || $language === 'Turco' || $language === 'Polaco' || $language === 'Neerlandés' || $language === 'Sueco' || $language === 'Alemán' || $language === 'Francés') {
// --- Traducir el título al inglés si el idioma es japonés ---
$translation_prompt = "Traduce el siguiente texto al inglés: '$title'. No saludes AL EMPEZAR, NO SALUDES al finalizar, solo devuelve la traducción.";
$data_translation = [
'model' => $model,
'messages' => [
['role' => 'system', 'content' => 'Eres un traductor experto ingles.'],
['role' => 'user', 'content' => $translation_prompt]
],
'temperature' => 0.90,
'max_tokens' => 1500,
];
$response_translation = tag_make_api_request($data_translation);
if (is_wp_error($response_translation)) {
error_log("Error al traducir el título: " . $response_translation->get_error_message());
// Manejo de errores... (puedes optar por no buscar productos en este caso)
} else {
$english_title = tag_filter_content($response_translation->choices[0]->message->content);
$english_title = str_replace('"', '', $english_title);
$products = search_amazon($english_title); // Buscar en Amazon con el título en inglés
}
} else {
$products = search_amazon($title); // Buscar en Amazon con el título original
}
$product_list = [];
if (!empty($products)) {
foreach ($products as $product) {
try {
if (isset($product['url']) && strpos($product['url'], '/dp/') !== false && isset($product['title'])) {
$domain_suffix = get_amazon_domain_suffix();
$asin = explode('/dp/', $product['url'])[1];
$asin = explode('/', $asin)[0];
$apilink = "https://www.amazon.{$domain_suffix}/dp/{$asin}?tag={$id_afiliado}"; // Reemplaza {id_afiliado} con tu ID de afiliado
$stars = $product['stars'] ?? 'N/A';
$total_reviews = $product['total_reviews'] ?? '5/5';
// Limpiar el título del producto
$clean_title = str_replace(['|', '"', '/', '{', '}', '[', ']', '(', ')'], '', $product['title']);
// Construir el shortcode
$product_shortcode = "[amazon nombre=\"{$clean_title}\" enlace=\"{$apilink}\" imagen=\"{$product['image']}\"]";
// Solo agregar el shortcode a la lista
$product_list[] = $product_shortcode;
}
} catch (Exception $e) {
continue;
}
}
// Unir todos los shortcodes en una sola cadena
$Hamazon = implode("\n", $product_list);
}
// --- Generar respuestas para las preguntas de Serper con Together (solo si hay preguntas) ---
$questions_and_answers = "";
if ($related_questions) {
foreach ($related_questions as $question) {
// Generar la respuesta con Together
$question_prompt = get_option('tag_question_prompt', "Por favor, no me saludes, Solo Responde Detalladamente en: '{$language}', la siguiente pregunta: {$question}. Y agrega también 3 Subtítulos H3 Relacionados en '{$language}' sobre {$question} en HTML '<h3></h3>' y responde cada Subtítulo h3 de forma detallada con un párrafo y un formato lista para enriquecer el texto: '<ol><li></li><li></li><li></li></ol>', en '{$language}', y pon en negrita en HTML '<strong></strong>' las palabras más relevantes. No saludes al finalizar, NO HAGAS NINGUNA CONCLUSION, no hables ningún otro idioma que no fuera solo '{$language}'.");
$question_prompt = str_replace(['$question', '$language'], [$question, $language], $question_prompt);
$data_question = array(
'model' => $model,
'messages' => array(
array('role' => 'system', 'content' => $system_prompt),
array('role' => 'user', 'content' => $question_prompt)
),
'temperature' => 0.90, // Puedes ajustar la temperatura
'max_tokens' => 2000, // Puedes ajustar el número máximo de tokens
);
$response_question = tag_make_api_request($data_question);
if (is_wp_error($response_question)) {
error_log("Error en la generación de la respuesta para la pregunta: " . $question . " - " . $response_question->get_error_message());
continue;
}
$answer = tag_filter_content($response_question->choices[0]->message->content);
$answer = str_replace($question, '', $answer);
// Eliminar todas las preguntas de $related_questions de la respuesta:
foreach ($related_questions as $q) {
$answer = str_replace($q, '', $answer);
}
// Obtener la imagen relacionada (solo si hay API keys configuradas)
if (!empty($serper_api_keys)) {
$image_url = tag_call_google_imagen_dev($question);
} else {
$image_url = null;
}
$image_html = $image_url ? "<p style='text-align: center;'><img src='" . esc_url($image_url) . "' style='display: block; margin: 0 auto;'></p>" : "";
// Combinar la pregunta, la imagen y la respuesta
$questions_and_answers .= "<h2>" . esc_html($question) . "</h2>" . $image_html . $answer;
}
}
// -------------------------------------------------------------------------------------------
// --- Verificar si el título de la cola ya ha sido procesado ---
if ($title === $tag_last_processed_title) {
error_log("El título de la cola '$title' ya ha sido procesado en esta ejecución.");
update_option('tag_article_queue', $queue); // Actualizar la cola
return; // Detener la función
}
// --- Si $Hamazon está vacío, vaciar también $prompt_h2 ---
if (empty($Hamazon)) {
$prompt_h2_arreglado = "";
}
// Verificar si el contenido tiene datos antes de crear el post
if (!empty($intro) && !empty($questions_and_answers) && !empty($faqs)) {
// Crear el post en WordPress.
$post_data = array(
'post_title' => sanitize_text_field($titulo_seo_post),
'post_content' => wp_kses_post("$intro\n\n$prompt_h2_arreglado\n\n$Hamazon\n\n$questions_and_answers\n\n$youtube_video_url\n\n<h2>$faqs_title</h2>\n$faqs"),
'post_status' => get_option('tag_post_status', 'publish'), // Obtener el estado del post
'post_author' => 1, // ID del autor. Cambiar según sea necesario.
'post_category' => array(get_cat_ID($categoria_elegida)) // Asignar la categoría por ID usando get_cat_ID
);
// Insertar el post
$post_id = wp_insert_post($post_data);
// Manejar el resultado de la inserción
if (is_wp_error($post_id)) {
// Manejar errores al crear el post
error_log("Error al crear el post: " . $post_id->get_error_message());
// Agregar el título a la lista de fallidos
$failed_titles[] = trim($title);
update_option('tag_failed_titles', array_unique($failed_titles));
// *** AÑADIR EL CÓDIGO AQUÍ ***
// Eliminar el título de la lista de títulos consultados a Serper
$serper_consulted_titles = get_option('tag_serper_consulted_titles', array());
if (($key = array_search($title, $serper_consulted_titles)) !== false) {
unset($serper_consulted_titles[$key]);
}
update_option('tag_serper_consulted_titles', $serper_consulted_titles);
// *** FIN DEL CÓDIGO AÑADIDO ***
//
} else {
// Guardar el título de la cola como último procesado
$tag_last_processed_title = $title;
// Agregar título a la lista de títulos creados
$created_titles[] = trim($title);
update_option('tag_created_titles', array_unique($created_titles)); // CORREGIDO
// Registrar el éxito en los logs (opcional)
error_log("Post creado con éxito: " . get_edit_post_link($post_id) . " - " . esc_html($title));
}
} else {
// Manejar caso de contenido vacío
error_log("No se puede crear el post, el contenido está vacío para el título: " . esc_html($title));
// Agregar el título a la lista de fallidos
$failed_titles[] = trim($title);
update_option('tag_failed_titles', array_unique($failed_titles));
// *** AÑADIR EL CÓDIGO AQUÍ ***
// Eliminar el título de la lista de títulos consultados a Serper
$serper_consulted_titles = get_option('tag_serper_consulted_titles', array());
if (($key = array_search($title, $serper_consulted_titles)) !== false) {
unset($serper_consulted_titles[$key]);
}
update_option('tag_serper_consulted_titles', $serper_consulted_titles);
// *** FIN DEL CÓDIGO AÑADIDO ***
}
// Actualizar la cola
update_option('tag_article_queue', $queue);
}
}
function tag_schedule_article_generation() {
// Obtén el tiempo de espera de la base de datos
$delay = get_option('tag_article_delay', 60); // Valor por defecto de 60 segundos si no está configurado
// Añade o actualiza los eventos cron con el intervalo configurado
if (!wp_next_scheduled('tag_generate_directorio_cron')) {
wp_schedule_event(time(), 'custom_interval', 'tag_generate_directorio_cron');
}
if (!wp_next_scheduled('tag_generate_articles_cron')) {
wp_schedule_event(time(), 'custom_interval', 'tag_generate_articles_cron');
}
if (!wp_next_scheduled('tag_generate_places_cron')) {
wp_schedule_event(time(), 'custom_interval', 'tag_generate_places_cron');
}
if (!wp_next_scheduled('tag_generate_amazon_sin_api_cron')) {
wp_schedule_event(time(), 'custom_interval', 'tag_generate_amazon_sin_api_cron');
}
}
add_action('wp', 'tag_schedule_article_generation');
function tag_add_cron_interval($schedules) {
$delay = get_option('tag_article_delay', 60); // Valor por defecto de 60 segundos si no está configurado
$schedules['custom_interval'] = array(
'interval' => $delay,
'display' => __('Custom Interval')
);
return $schedules;
}
add_filter('cron_schedules', 'tag_add_cron_interval');
// Desregistrar el cron que no corresponda al guardar las opciones de nicho
function tag_unschedule_incorrect_cron() {
if (is_directorio_niche()) {
// Nicho directorio
wp_clear_scheduled_hook('tag_generate_articles_cron');
wp_clear_scheduled_hook('tag_generate_places_cron');
wp_clear_scheduled_hook('tag_generate_amazon_sin_api_cron');
} elseif (is_reseñas_individuales_niche()) {
// Nicho de reseñas individuales
wp_clear_scheduled_hook('tag_generate_directorio_cron');
wp_clear_scheduled_hook('tag_generate_articles_cron');
wp_clear_scheduled_hook('tag_generate_amazon_sin_api_cron');
} elseif (is_amazon_sin_api_niche()) {
// Nicho de Amazon sin API
wp_clear_scheduled_hook('tag_generate_directorio_cron');
wp_clear_scheduled_hook('tag_generate_places_cron');
wp_clear_scheduled_hook('tag_generate_articles_cron');
} else {
// Si no es ninguno de los nichos anteriores, desprogramar por defecto
wp_clear_scheduled_hook('tag_generate_directorio_cron');
wp_clear_scheduled_hook('tag_generate_places_cron');
wp_clear_scheduled_hook('tag_generate_amazon_sin_api_cron');
}
}
// Añadir acciones para los tres tipos de nicho
add_action('update_option_tag_nicho_directorio', 'tag_unschedule_incorrect_cron');
add_action('update_option_tag_niche_type', 'tag_unschedule_incorrect_cron');
add_action('update_option_tag_amazon_sin_api_niche', 'tag_unschedule_incorrect_cron');
// Desregistrar eventos cron al desactivar el plugin
function tag_unschedule_article_generation() {
wp_clear_scheduled_hook('tag_generate_directorio_cron');
wp_clear_scheduled_hook('tag_generate_articles_cron');
wp_clear_scheduled_hook('tag_generate_places_cron');
wp_clear_scheduled_hook('tag_generate_amazon_sin_api_cron');
}
register_deactivation_hook(__FILE__, 'tag_unschedule_article_generation');
// Acciones para ejecutar las funciones de procesamiento de colas
add_action('tag_generate_directorio_cron', 'tag_process_directorio_queue');
add_action('tag_generate_articles_cron', 'tag_process_article_queue');
add_action('tag_generate_places_cron', 'tag_process_places_queue');
add_action('tag_generate_amazon_sin_api_cron', 'tag_process_amazon_sin_api_queue');
// Mostrar la página de administración.
function tag_admin_page() {
// Obtener los títulos creados almacenados.
$created_titles = get_option('tag_created_titles', array());
$api_key = get_option('tag_api_key');
$delay = get_option('tag_article_delay', 60); // Valor por defecto de 60 segundos (1 minuto).
$categories = get_option('tag_categories', '');
// Idiomas disponibles
$languages = array(
'Español' => 'Español',
'Inglés' => 'Inglés',
'Turco' => 'Turco',
'Árabe' => 'Árabe',
'Hindi' => 'Hindi',
'Ruso' => 'Ruso',
'Francés' => 'Francés',
'Portugués' => 'Portugués',
'Alemán' => 'Alemán',
'Polaco' => 'Polaco',
'Japonés' => 'Japonés',
'Coreano' => 'Coreano',
'Sueco' => 'Sueco',
'Indonesio' => 'Indonesio',
'Italiano' => 'Italiano',
'Hebreo' => 'Hebreo',
'Chino Mandarín' => 'Chino Mandarín',
'Neerlandés' => 'Neerlandés',
'Vietnamita' => 'Vietnamita',
'Sundanés' => 'Sundanés',
'Español (Perú)' => 'Español (Perú)',
'Español (México)' => 'Español (México)',
'Español (Argentina)' => 'Español (Argentina)',
'Español (Chile)' => 'Español (Chile)',
'Español (Colombia)' => 'Español (Colombia)',
'Español (Ecuador)' => 'Español (Ecuador)',
'Español (Venezuela)' => 'Español (Venezuela)',
'Español (Guatemala)' => 'Español (Guatemala)',
'Español (Cuba)' => 'Español (Cuba)',
'Español (Bolivia)' => 'Español (Bolivia)',
'Español (Haití)' => 'Español (Haití)',
'Español (República Dominicana)' => 'Español (República Dominicana)',
'Español (Honduras)' => 'Español (Honduras)',
'Español (Paraguay)' => 'Español (Paraguay)',
'Español (Nicaragua)' => 'Español (Nicaragua)',
'Español (El Salvador)' => 'Español (El Salvador)',
'Español (Costa Rica)' => 'Español (Costa Rica)',
'Español (Puerto Rico)' => 'Español (Puerto Rico)',
'Español (Panamá)' => 'Español (Panamá)',
'Español (Uruguay)' => 'Español (Uruguay)',
'Español (Jamaica)' => 'Español (Jamaica)',
'Español (Trinidad y Tobago)' => 'Español (Trinidad y Tobago)',
'Español (Belice)' => 'Español (Belice)'
);
// Modelos disponibles por proveedor
$models_by_provider = array(
'groq' => array(
'llama3-groq-70b-8192-tool-use-preview' => 'llama3-groq-70b-8192-tool-use-preview',
'llama3-70b-8192' => 'llama3-70b-8192'
),
'together' => array(
'Qwen/Qwen2.5-72B-Instruct-Turbo' => 'Qwen/Qwen2.5-72B-Instruct-Turbo',
'meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo' => 'meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo',
'meta-llama/Llama-3-70b-chat-hf' => 'meta-llama/Llama-3-70b-chat-hf',
'meta-llama/Meta-Llama-3.1-405B-Instruct-Turbo' => 'meta-llama/Meta-Llama-3.1-405B-Instruct-Turbo'
),
'hyperbolic' => array(
'meta-llama/Meta-Llama-3.1-70B-Instruct' => 'meta-llama/Meta-Llama-3.1-70B-Instruct',
'Qwen/Qwen2.5-72B-Instruct' => 'Qwen/Qwen2.5-72B-Instruct',
'deepseek-ai/DeepSeek-V2.5' => 'deepseek-ai/DeepSeek-V2.5',
'meta-llama/Meta-Llama-3.1-405B-Instruct' => 'meta-llama/Meta-Llama-3.1-405B-Instruct'
),
'OpenAI' => array(
'gpt-4o' => 'gpt-4o',
'gpt-4o-mini' => 'gpt-4o-mini'
),
'SambanovaAI' => array(
'Meta-Llama-3.1-70B-Instruct' => 'Meta-Llama-3.1-70B-Instruct',
'Meta-Llama-3.1-405B-Instruct' => 'Meta-Llama-3.1-405B-Instruct'
),
'FireworksAI' => array(
'accounts/fireworks/models/llama-v3p1-70b-instruct' => 'accounts/fireworks/models/llama-v3p1-70b-instruct',
'accounts/fireworks/models/llama-v3p1-405b-instruct' => 'accounts/fireworks/models/llama-v3p1-405b-instruct',
'accounts/fireworks/models/qwen2p5-72b-instruct' => 'accounts/fireworks/models/qwen2p5-72b-instruct'
),
'Cohere' => array(
'command-r-plus-08-2024' => 'command-r-plus-08-2024'
)
);
// Obtener el proveedor de API seleccionado en el panel de configuración
$api_provider = get_option('tag_api_provider', 'groq');
// Obtener los modelos para el proveedor seleccionado
$models = $models_by_provider[$api_provider];
if (isset($_POST['tag_save_settings'])) {
update_option('tag_api_key', sanitize_text_field($_POST['tag_api_key']));
update_option('tag_article_delay', intval($_POST['tag_article_delay']));
update_option('tag_categories', sanitize_text_field($_POST['tag_categories']));
update_option('tag_language', sanitize_text_field($_POST['tag_language']));
update_option('tag_faqs_title', sanitize_text_field($_POST['tag_faqs_title']));
update_option('tag_api_keys', sanitize_textarea_field($_POST['tag_api_keys']));
update_option('tag_model', sanitize_text_field($_POST['tag_model']));
update_option('tag_serper_api_keys', sanitize_textarea_field($_POST['tag_serper_api_keys']));
update_option('tag_api_provider', sanitize_text_field($_POST['tag_api_provider']));
update_option('tag_post_status', sanitize_text_field($_POST['tag_post_status']));
update_option('tag_scraping_dog_api_keys', sanitize_textarea_field($_POST['tag_scraping_dog_api_keys'])); // <-- Nueva línea
echo '<div class="updated"><p>Configuración guardada.</p></div>';
}
// Guardar la configuración de nicho
if (isset($_POST['tag_save_niche_settings'])) {
update_option('tag_nicho_directorio', sanitize_text_field($_POST['tag_nicho_directorio']));
update_option('tag_amazon_sin_api_niche', sanitize_text_field($_POST['tag_amazon_sin_api_niche'])); // <-- Nueva línea
update_option('tag_amazon_affiliate_id', sanitize_text_field($_POST['tag_amazon_affiliate_id'])); // <-- Nueva línea
update_option('tag_amazon_country', sanitize_text_field($_POST['tag_amazon_country'])); // <-- Nueva línea
update_option('tag_niche_type', sanitize_text_field($_POST['tag_niche_type']));
update_option('tag_tematica_directorio', sanitize_text_field($_POST['tag_tematica_directorio']));
echo '<div class="updated"><p>Configuración de nicho guardada.</p></div>';
}
$allowed_html = array(
'h2' => array(),
'h3' => array(),
'strong' => array(),
'em' => array(),
'table' => array(),
'thead' => array(),
'tbody' => array(),
'tr' => array(),
'th' => array(),
'td' => array(),
'p' => array(),
'br' => array(),
'ol' => array(), // <-- Añadir etiqueta <ol>
'li' => array() // <-- Añadir etiqueta <li>
);
if (isset($_POST['tag_save_prompts'])) {
update_option('tag_intro_prompt', wp_kses($_POST['tag_intro_prompt'], $allowed_html));
update_option('tag_subtitles_prompt', wp_kses($_POST['tag_subtitles_prompt'], $allowed_html));
update_option('tag_faqs_prompt', wp_kses($_POST['tag_faqs_prompt'], $allowed_html));
update_option('tag_question_prompt', wp_kses_post($_POST['tag_question_prompt'], $allowed_html));
update_option('tag_system_prompt', wp_kses_post($_POST['tag_system_prompt'], $allowed_html));
echo '<div class="updated"><p>Prompts guardados correctamente.</p></div>';
}
if (isset($_POST['tag_generate_articles'])) {
$titles = explode("\n", sanitize_textarea_field($_POST['tag_article_titles']));
$queue = get_option('tag_article_queue', array());
foreach ($titles as $title) {
if (in_array(trim($title), $created_titles)) {
continue; // Saltar títulos ya creados.
}
$queue[] = trim($title);
}
update_option('tag_article_queue', $queue);
echo '<div class="updated"><p>Artículos añadidos a la cola de generación.</p></div>';
}
// Acción para borrar títulos creados
if (isset($_POST['tag_clear_created_titles'])) {
update_option('tag_created_titles', array());
update_option('tag_serper_consulted_titles', array()); // <-- Nueva línea para borrar títulos consultados a Serper
echo '<div class="updated"><p>Títulos creados y consultados a Serper eliminados con éxito.</p></div>';
}
// Acción para borrar títulos fallidos
if (isset($_POST['tag_clear_failed_titles'])) {
update_option('tag_failed_titles', array());
update_option('tag_serper_consulted_titles', array()); // <-- Nueva línea para borrar títulos consultados a Serper
echo '<div class="updated"><p>Títulos fallidos y consultados a Serper eliminados con éxito.</p></div>';
}
// Acción para limpiar la cola
if (isset($_POST['tag_clear_queue'])) {
update_option('tag_article_queue', array());
update_option('tag_serper_consulted_titles', array()); // <-- Nueva línea para borrar títulos consultados a Serper
echo '<div class="updated"><p>Cola de artículos limpiada.</p></div>';
}
// Convertir títulos creados a formato de texto para mostrar en la segunda caja.
$created_titles_text = implode("\n", $created_titles);
// Mostrar la caja de texto con los títulos pendientes
$queue = get_option('tag_article_queue', array());
$queue_titles = implode("\n", $queue);
// Mostrar la caja de texto con los títulos fallidos
$failed_titles = get_option('tag_failed_titles', array());
$failed_titles_text = implode("\n", $failed_titles);
?>
<div class="wrap">
<h1>SharkContents Article Generator</h1>
<h2 class="nav-tab-wrapper">
<a href="?page=SharkContents-article-generator&tab=settings" class="nav-tab <?php echo !isset($_GET['tab']) || $_GET['tab'] == 'settings' ? 'nav-tab-active' : ''; ?>">Configuración</a>
<a href="?page=SharkContents-article-generator&tab=niche_settings" class="nav-tab <?php echo isset($_GET['tab']) && $_GET['tab'] == 'niche_settings' ? 'nav-tab-active' : ''; ?>">Configuración de Tipo de Nicho</a>
<a href="?page=SharkContents-article-generator&tab=prompts" class="nav-tab <?php echo isset($_GET['tab']) && $_GET['tab'] == 'prompts' ? 'nav-tab-active' : ''; ?>">Prompts</a>
<a href="?page=SharkContents-article-generator&tab=generate" class="nav-tab <?php echo isset($_GET['tab']) && $_GET['tab'] == 'generate' ? 'nav-tab-active' : ''; ?>">Generar Artículos</a>
</h2>
<?php if (!isset($_GET['tab']) || $_GET['tab'] == 'settings') : ?>
<!-- Pestaña de Configuración -->
<form method="post" style="background-color: #fff; padding: 20px; border-radius: 8px;">
<table class="form-table">
<tr>
<th scope="row"><label for="tag_api_keys" style="font-weight: bold;">APIS Dependiendo que Proveedor USARAS (una por línea)</label></th>
<td><textarea name="tag_api_keys" id="tag_api_keys" rows="5" class="large-text"><?php echo esc_textarea(get_option('tag_api_keys')); ?></textarea></td>
</tr>
<tr>
<th scope="row"><label for="tag_serper_api_keys" style="font-weight: bold;">API Keys de Serper (una por línea)</label></th>
<td><textarea name="tag_serper_api_keys" id="tag_serper_api_keys" rows="5" class="large-text"><?php echo esc_textarea(get_option('tag_serper_api_keys')); ?></textarea></td>
</tr>
<tr>
<th scope="row"><label for="tag_scraping_dog_api_keys" style="font-weight: bold;">API Keys de ScrapingDog (una por línea)</label></th>
<td><textarea name="tag_scraping_dog_api_keys" id="tag_scraping_dog_api_keys" rows="5" class="large-text"><?php echo esc_textarea(get_option('tag_scraping_dog_api_keys')); ?></textarea></td>
</tr>
<tr>
<th scope="row"><label for="tag_article_delay" style="font-weight: bold;">Tiempo de Espera entre Artículos (en segundos)</label></th>
<td><input name="tag_article_delay" type="number" id="tag_article_delay" value="<?php echo esc_attr($delay); ?>" class="small-text"></td>
</tr>
<tr>
<th scope="row"><label for="tag_categories" style="font-weight: bold;">Categorías (separadas por "|")</label></th>
<td><input name="tag_categories" type="text" id="tag_categories" value="<?php echo esc_attr(get_option('tag_categories', '')); ?>" class="regular-text"></td>
</tr>
<tr>
<th scope="row"><label for="tag_language" style="font-weight: bold;">Idioma</label></th>
<td>
<select name="tag_language" id="tag_language">
<?php foreach ($languages as $value => $label) : ?>
<option value="<?php echo esc_attr($value); ?>" <?php selected(get_option('tag_language', 'Español'), $value); ?>><?php echo esc_html($label); ?></option>
<?php endforeach; ?>
</select>
</td>
</tr>
<tr>
<th scope="row"><label for="tag_faqs_title" style="font-weight: bold;">Título del H2 de Preguntas Frecuentes</label></th>
<td>
<input type="text" name="tag_faqs_title" id="tag_faqs_title" value="<?php echo esc_attr(get_option('tag_faqs_title', 'Preguntas Frecuentes')); ?>" class="regular-text">
</td>
</tr>
<tr>
<th scope="row"><label for="tag_api_provider" style="font-weight: bold;">Proveedor de API</label></th>
<td>
<select name="tag_api_provider" id="tag_api_provider">
<option value="groq" <?php selected(get_option('tag_api_provider', 'groq'), 'groq'); ?>>groq</option>
<option value="together" <?php selected(get_option('tag_api_provider', 'groq'), 'together'); ?>>Together</option>
<option value="hyperbolic" <?php selected(get_option('tag_api_provider', 'groq'), 'hyperbolic'); ?>>Hyperbolic</option>
<option value="OpenAI" <?php selected(get_option('tag_api_provider', 'groq'), 'OpenAI'); ?>>OpenAI</option>
<option value="SambanovaAI" <?php selected(get_option('tag_api_provider', 'groq'), 'SambanovaAI'); ?>>SambanovaAI</option>
<option value="FireworksAI" <?php selected(get_option('tag_api_provider', 'groq'), 'FireworksAI'); ?>>FireworksAI</option>
<option value="Cohere" <?php selected(get_option('tag_api_provider', 'groq'), 'Cohere'); ?>>Cohere</option>
</select>
</td>
</tr>
<tr>
<th scope="row"><label for="tag_model" style="font-weight: bold;">Modelo</label></th>
<td>
<select name="tag_model" id="tag_model">
<?php foreach ($models as $value => $label) : ?>
<option value="<?php echo esc_attr($value); ?>" <?php selected(get_option('tag_model'), $value); ?>><?php echo esc_html($label); ?></option>
<?php endforeach; ?>
</select>
</td>
</tr>
<tr>
<th scope="row"><label for="tag_post_status" style="font-weight: bold;">Estado del post</label></th>
<td>
<select name="tag_post_status" id="tag_post_status">
<option value="publish" <?php selected(get_option('tag_post_status', 'publish'), 'publish'); ?>>Publicado</option>
<option value="draft" <?php selected(get_option('tag_post_status', 'publish'), 'draft'); ?>>Borrador</option>
</select>
</td>
</tr>
</table>
<div style="display: flex; align-items: center; gap: 10px; margin-top: 20px;">
<?php submit_button('Guardar Configuración', 'primary', 'tag_save_settings'); ?>
<input type="submit" name="tag_delete_data" class="button button-secondary" id="tag_delete_data" value="Eliminar datos del plugin">
</div>
</form>
<div style="background-color: #f7f7f7; padding: 10px; margin-top: 20px; border-left: 4px solid #007cba; border-radius: 4px;">
<p style="margin: 0; font-size: 14px;">
<strong>Instrucciones:</strong><br>
1. Selecciona un proveedor de API de la lista a continuación, haz clic en "Guardar Configuración" y recarga la página:<br>
- <a href="https://groq.com" target="_blank">Groq - Por Mientas Gratis</a><br>
- <a href="https://www.together.ai/" target="_blank">Together - Regalan 5$ Por cuenta Creada - Aceptan Tarjeta de DEBITO Virtual</a><br>
- <a href="https://hyperbolic.xyz" target="_blank">Hyperbolic - Regalan 10$ Por cuenta Creada</a><br>
- <a href="https://openai.com" target="_blank">OpenAI - DE PAGO</a><br>
- <a href="https://fireworks.ai" target="_blank">FireworksAI - Regalan 1$ Por cuenta Creada - Aceptan Tarjeta Virtual</a><br>
- <a href="https://sambanova.ai" target="_blank">SambanovaAI - Por Mientas Gratis</a><br>
2. Después de recargar, elige el modelo que deseas utilizar del proveedor de API seleccionado y haz clic nuevamente en "Guardar Configuración".<br>
3. ¡Listo! Ahora el plugin funcionará correctamente con el modelo seleccionado.<br>
4. Si estás generando artículos informacionales o artículos de Amazon Afiliados Sin API, puedes agregar varias categorías que hayas creado previamente.<br>
5. Para artículos de tipo "Post Pilar Directorio", solo puedes seleccionar una categoría, por ejemplo, "Provincias de España".<br>
6. Para artículos de tipo "Reseñas Individuales Directorio", también debes seleccionar solo una categoría, por ejemplo, "Fichas de Veterinarias".<br>
7. Asegúrate de configurar el CRON correctamente: <a href="https://AQUITUWEB/wp-cron.php?doing_cron" target="_blank">https://AQUITUWEB/wp-cron.php?doing_cron</a>, en <a href="https://console.cron-job.org/" target="_blank">https://console.cron-job.org/</a>.<br>
8.- Tambien Puedes Configurar el Cron en tu servidor cada Minuto, mediante este comando: wget -O /dev/null https://AQUITUWEB/wp-cron.php?doing_cron > /dev/null 2>&1<br>
</p>
</div>
<div style="background-color: #f7f7f7; padding: 10px; margin-top: 20px; border-left: 4px solid #007cba; border-radius: 4px;">
<p style="margin: 0; font-size: 14px;">
<strong>Instructions:</strong><br>
1. Select an API provider from the list below, click on "Save Settings," and refresh the page:<br>
- <a href="https://groq.com" target="_blank">Groq - Free for a limited time</a><br>
- <a href="https://www.together.ai/" target="_blank">Together - $5 credit for account creation</a><br>
- <a href="https://hyperbolic.xyz" target="_blank">Hyperbolic - $10 credit for account creation</a><br>
- <a href="https://openai.com" target="_blank">OpenAI - Paid service</a><br>
- <a href="https://fireworks.ai" target="_blank">FireworksAI - Give away $1 Per Created Account - Virtual Card Accepted</a><br>
- <a href="https://sambanova.ai" target="_blank">SambanovaAI - For Free Mientas</a><br>
2. After refreshing, choose the model you want to use from the selected API provider and click "Save Settings" again.<br>
3. Done! The plugin will now work correctly with the selected model.<br>
4. If you are generating informational articles or Amazon Affiliate articles without an API, you can add multiple categories that you have previously created.<br>
5. For "Pillar Post Directory" articles, you can only select one category, such as "Provinces of Spain".<br>
6. For "Individual Reviews Directory" articles, you should also select only one category, such as "Veterinary Listings".<br>
7. Make sure to configure the CRON correctly: <a href="https://AQUITUWEB/wp-cron.php?doing_cron" target="_blank">https://AQUITUWEB/wp-cron.php?doing_cron</a>, at <a href="https://console.cron-job.org/" target="_blank">https://console.cron-job.org/</a>.<br>
8. You can also configure the CRON on your server to run every minute using the following command: wget -O /dev/null https://YOU-WEBSITE/wp-cron.php?doing_cron > /dev/null 2>&1<br>
</p>
</div>
<?php
// Manejar el botón para eliminar los datos
if (isset($_POST['tag_delete_data'])) {
tag_uninstall();
echo '<div class="updated"><p>Datos del plugin eliminados.</p></div>';
}
?>
<?php elseif (isset($_GET['tab']) && $_GET['tab'] == 'niche_settings') : ?>
<!-- Pestaña de Configuración de Nicho -->
<form method="post" style="background-color: #fff; padding: 20px; border-radius: 8px;">
<table class="form-table">
<tr>
<th scope="row"><label for="tag_amazon_country" style="font-weight: bold;">País de Amazon</label></th>
<td>
<select name="tag_amazon_country" id="tag_amazon_country">
<?php
$amazon_countries = [
"USA", "España", "Japón", "Alemania", "Australia",
"Brasil", "Canadá", "Francia", "India", "Italia",
"México", "Países Bajos", "Arabia Saudita", "Singapur",
"Turquía", "Emiratos Árabes Unidos", "Reino Unido", "Polonia", "Egipto", "Suecia"
];
foreach ($amazon_countries as $country) : ?>
<option value="<?php echo esc_attr($country); ?>" <?php selected(get_option('tag_amazon_country'), $country); ?>><?php echo esc_html($country); ?></option>
<?php endforeach; ?>
</select>
</td>
</tr>
<tr>
<th scope="row"><label for="tag_amazon_sin_api_niche" style="font-weight: bold;">¿Es Nicho Amazon Sin API?</label></th>
<td>
<select name="tag_amazon_sin_api_niche" id="tag_amazon_sin_api_niche">
<option value="no" <?php selected(get_option('tag_amazon_sin_api_niche'), 'no'); ?>>No</option>
<option value="si" <?php selected(get_option('tag_amazon_sin_api_niche'), 'si'); ?>>Sí</option>
</select>
</td>
</tr>
<tr>
<th scope="row"><label for="tag_amazon_affiliate_id" style="font-weight: bold;">ID de Afiliado de Amazon</label></th>
<td>
<input type="text" name="tag_amazon_affiliate_id" id="tag_amazon_affiliate_id" value="<?php echo esc_attr(get_option('tag_amazon_affiliate_id', '')); ?>" class="regular-text">
</td>
</tr>
<tr>
<th scope="row"><label for="tag_nicho_directorio" style="font-weight: bold;">¿Es Post Pilar de directorio?</label></th>
<td>
<select name="tag_nicho_directorio" id="tag_nicho_directorio">
<option value="si" <?php selected(get_option('tag_nicho_directorio', 'no'), 'si'); ?>>Sí</option>
<option value="no" <?php selected(get_option('tag_nicho_directorio', 'no'), 'no'); ?>>No</option>
</select>
</td>
</tr>
<tr>
<th scope="row"><label for="tag_niche_type" style="font-weight: bold;">¿Es Posts de reseñas individuales?</label></th>
<td>
<select name="tag_niche_type" id="tag_niche_type">
<option value="si" <?php selected(get_option('tag_niche_type', 'no'), 'si'); ?>>Sí</option>
<option value="no" <?php selected(get_option('tag_niche_type', 'no'), 'no'); ?>>No</option>
</select>
</td>
</tr>
<tr>
<th scope="row"><label for="tag_tematica_directorio" style="font-weight: bold;">Temática del directorio</label></th>
<td><input type="text" name="tag_tematica_directorio" id="tag_tematica_directorio" value="<?php echo esc_attr(get_option('tag_tematica_directorio')); ?>" class="regular-text"></td>
</tr>
</table>
<?php submit_button('Guardar Configuración de Nicho', 'primary', 'tag_save_niche_settings'); ?>
<!-- Mensaje de instrucciones después del formulario -->
<div style="background-color: #f7f7f7; padding: 10px; margin-top: 20px; border-left: 4px solid #007cba; border-radius: 4px;">
<p style="margin: 0; font-size: 14px;">
<strong>Instrucciones:</strong><br>
1. Si seleccionas "Post Pilar Directorio", desactiva las opciones de "Amazon Sin API" y "Reseñas Individuales". Puedes ingresar un título como "Mejores Veterinarias en Madrid". Esto generará un post pilar.<br>
2. Si seleccionas "Reseñas Individuales", desactiva las opciones de "Amazon Sin API" y "Post Pilar Directorio", asegúrate de ingresar un título como "Mejores Veterinarias en Madrid". Esto va a Scrapear 10 negocios de ese titulo y creara 10 posts creados para cada titulo que pongas en la cola.<br>
3. Si seleccionas "Nicho de Amazon Afiliados Sin API", desactiva las opciones de "Directorio". Selecciona el país para el scrapeo y usa títulos basados en palabras clave como "Cafeteras Italianas". La IA optimizará el título automáticamente y ScrapingDog extraerá los 5 mejores productos para hacer un buen post seo optimizado. - (Debes Poner la API - REGALAN 1000 CREDITOS, RECUERDA QUE CUESTA 10 CREDITOS POR SCRAPEO)<br>
4. Si deseas unicamente "Artículos Informacionales", desactiva todas las opciones de esta pestaña. Haz clic en "Guardar" para aplicar los cambios.<br>
5. Recuerda que debes tener una API de Serper.dev con saldo suficiente para evitar errores en los nichos de directorios. Puedes adquirir créditos por $50 (50,000 créditos) en Serper.dev.
</p>
</div>
<div style="background-color: #f7f7f7; padding: 10px; margin-top: 20px; border-left: 4px solid #007cba; border-radius: 4px;">
<p style="margin: 0; font-size: 14px;">
<strong>Instructions:</strong><br>
1. If you select "Directory Pillar Post", disable the "Amazon Without API" and "Individual Reviews" options. You can enter a title like "Best Veterinarians in Madrid". This will generate a pillar post.<br>
2. If you select "Individual Reviews", disable the "Amazon Without API" and "Directory Pillar Post" options, and make sure to enter a title like "Best Veterinarians in Madrid". This will scrape 10 businesses for that title and create 10 posts for each title you queue.<br>
3. If you select "Amazon Affiliate Niche Without API", disable the "Directory" option. Select the country for scraping and use keyword-based titles like "Italian Coffee Makers". The AI will automatically optimize the title, and ScrapingDog will extract the top 5 products to create a well-optimized SEO post. - (You need to provide the API - THEY GIVE 1000 CREDITS, REMEMBER THAT IT COSTS 10 CREDITS PER SCRAPE)<br>
4. If you only want "Informational Articles", disable all options on this tab. Click "Save" to apply the changes.<br>
5. Remember to have an active Serper.dev API with sufficient balance to avoid errors in directory niches. You can purchase credits for $50 (50,000 credits) at Serper.dev.
</p>
</div>
</div>
</form>
<?php elseif (isset($_GET['tab']) && $_GET['tab'] == 'prompts') : ?>
<!-- Pestaña de Prompts -->
<form method="post" style="background-color: #fff; padding: 20px; border-radius: 8px;">
<table class="form-table">
<tr>
<th scope="row"><label for="tag_intro_prompt" style="font-weight: bold;">Prompt de Introducción</label></th>
<td>
<textarea name="tag_intro_prompt" id="tag_intro_prompt" rows="5" class="large-text"><?php echo stripslashes(get_option('tag_intro_prompt')); ?></textarea>
<p class="description">Prompt para generar la introducción del artículo. Puedes usar la variable <code>$title</code> para insertar el título del artículo. También puedes usar <code>$language</code> para el idioma.</p>
</td>
</tr>
<tr>
<th scope="row"><label for="tag_subtitles_prompt" style="font-weight: bold;">Prompt de Subtítulos</label></th>
<td>
<textarea name="tag_subtitles_prompt" id="tag_subtitles_prompt" rows="5" class="large-text"><?php echo stripslashes(get_option('tag_subtitles_prompt')); ?></textarea>
<p class="description">Prompt para generar los subtítulos del artículo. Puedes usar la variable <code>$title</code> para insertar el título del artículo. También puedes usar <code>$language</code> para el idioma.</p>
</td>
</tr>
<tr>
<th scope="row"><label for="tag_faqs_prompt" style="font-weight: bold;">Prompt de Preguntas Frecuentes</label></th>
<td>
<textarea name="tag_faqs_prompt" id="tag_faqs_prompt" rows="5" class="large-text"><?php echo stripslashes(get_option('tag_faqs_prompt')); ?></textarea>
<p class="description">Prompt para generar las preguntas frecuentes del artículo. Puedes usar la variable <code>$title</code> para insertar el título del artículo. También puedes usar <code>$language</code> para el idioma.</p>
</td>
</tr>
<tr>
<th scope="row"><label for="tag_question_prompt" style="font-weight: bold;">Prompt para PPA de Google</label></th>
<td>
<textarea name="tag_question_prompt" id="tag_question_prompt" rows="5" class="large-text"><?php echo stripslashes(get_option('tag_question_prompt')); ?></textarea>
<p class="description">Prompt para PPA de Google. Puedes usar las variables <code>$question</code> y <code>$language</code>.</p>
</td>
</tr>
<tr>
<th scope="row"><label for="tag_system_prompt" style="font-weight: bold;">Prompt del Sistema</label></th>
<td>
<textarea name="tag_system_prompt" id="tag_system_prompt" rows="5" class="large-text"><?php echo stripslashes(get_option('tag_system_prompt')); ?></textarea>
<p class="description">Prompt del sistema. Usado para configurar el estilo de escritura del redactor. Puedes usar <code>$language</code> para insertar el idioma.</p>
</td>
</tr>
</table>
<?php submit_button('Guardar Prompts', 'primary', 'tag_save_prompts'); ?>
</form>
<?php elseif (isset($_GET['tab']) && $_GET['tab'] == 'generate') : ?>
<!-- Pestaña de Generar Artículos -->
<form method="post">
<table class="form-table">
<tr>
<th scope="row"><label for="tag_article_titles" style="font-weight: bold;">Títulos de Artículos (uno por línea)</label></th>
<td><textarea name="tag_article_titles" id="tag_article_titles" rows="10" class="large-text"><?php echo esc_textarea($queue_titles); ?></textarea></td>
</tr>
</table>
<div style="display: flex; align-items: center; gap: 10px; margin-top: 20px;">
<?php submit_button('Generar Artículos', 'primary', 'tag_generate_articles'); ?>
<form method="post" style="display: inline-block;">
<input type="submit" name="tag_clear_queue" class="button" style="background-color: #d9534f; border-color: #d43f3a; color: #fff; font-weight: bold;" value="Limpiar Cola">
</form>
<!-- --- FORMULARIOS PARA ELIMINAR TITULOS (COLOR ROJO) --- -->
<form method="post" style="display: inline-block;">
<input type="submit" name="tag_clear_failed_titles" class="button" style="background-color: #d9534f; border-color: #d43f3a; color: #fff; font-weight: bold;" value="Eliminar Títulos Fallidos">
</form>
<form method="post" style="display: inline-block;">
<input type="submit" name="tag_clear_created_titles" class="button" style="background-color: #d9534f; border-color: #d43f3a; color: #fff; font-weight: bold;" value="Eliminar Títulos Creados">
</form>
</div>
</form>
<h2>Títulos en Cola</h2>
<textarea rows="10" class="large-text" readonly><?php echo esc_textarea($queue_titles); ?></textarea>
<h2>Títulos Creados</h2>
<textarea rows="10" class="large-text" readonly><?php echo esc_textarea($created_titles_text); ?></textarea>
<h2>Artículos Fallidos</h2>
<textarea rows="10" class="large-text" readonly><?php echo esc_textarea($failed_titles_text); ?></textarea>
<?php endif; ?>
</div>
<style>
/* Estilos adicionales para .wrap */
.wrap h1 {
text-align: center; /* Centrar el h1 */
}
/* Estilos para las pestañas */
.nav-tab-wrapper a {
background-color: #007cba; /* Fondo azul para las pestañas */
color: #fff; /* Letras blancas para las pestañas */
}
.nav-tab-wrapper a.nav-tab-active {
background-color: #00578a; /* Fondo azul más oscuro para la pestaña activa */
color: #fff; /* Letras blancas para la pestaña activa */
}
/* Estilos generales */
.wrap {
font-family: Arial, sans-serif;
background-color: #ffff;
}
.nav-tab-active {
background-color: #007cba;
color: #fff;
}
/* Pestaña de Configuración */
#settings-tab .form-table th {
font-weight: bold;
font-size: 16px;
color: #222;
padding: 10px 0;
}
#settings-tab .form-table td {
padding: 10px 0;
}
#settings-tab textarea.large-text {
background-color: #f9f9f9;
border: 1px solid #ccc;
padding: 10px;
font-size: 14px;
width: 100%;
}
#settings-tab input[type="submit"].button-primary {
background-color: #0073aa;
border-color: #006799;
color: #fff;
font-weight: bold;
}
#tag_delete_data {
background-color: #d9534f;
border-color: #d43f3a;
color: #fff;
font-weight: bold;
}
#tag_delete_data.button-secondary:hover {
background-color: #c9302c;
border-color: #ac2925;
}
/* Pestaña de Generar Artículos */
#generate-tab {
background-color: #fff;
padding: 20px;
border-radius: 8px;
}
#generate-tab h2 {
font-size: 24px;
font-weight: bold;
color: #444;
margin-bottom: 15px;
}
#generate-tab .form-table th {
font-weight: bold;
font-size: 16px;
color: #222;
padding: 10px 0;
}
#generate-tab .form-table td {
padding: 10px 0;
}
#generate-tab textarea.large-text {
background-color: #f9f9f9;
border: 1px solid #ccc;
padding: 10px;
font-size: 14px;
width: 100%;
}
#generate-tab input[type="submit"].button-primary {
background-color: #0073aa;
border-color: #006799;
color: #fff;
font-weight: bold;
}
#generate-tab input[type="submit"].button-secondary {
background-color: #d63638;
border-color: #a71c1c;
color: #fff;
font-weight: bold;
}
/* Estilos adicionales para la pestaña "Generar" */
/* Fondo blanco y letras en negrita */
#generate-tab {
background-color: #fff; /* Fondo blanco */
}
#generate-tab label,
#generate-tab th,
#generate-tab h2,
#generate-tab legend {
font-weight: bold; /* Letras en negrita */
}
</style>
<?php
}
// Función para eliminar todos los datos del plugin al desinstalar
function tag_uninstall() {
// Eliminar opciones guardadas en la base de datos
delete_option('tag_api_keys');
delete_option('tag_serper_api_keys');
delete_option('tag_article_delay');
delete_option('tag_categories');
delete_option('tag_language');
delete_option('tag_faqs_title');
delete_option('tag_model');
delete_option('tag_post_status');
delete_option('tag_article_queue');
delete_option('tag_created_titles');
delete_option('tag_failed_titles');
delete_option('tag_nicho_directorio');
delete_option('tag_tematica_directorio');
delete_option('tag_niche_type');
delete_option('tag_scraping_dog_api_keys');
delete_option('tag_amazon_country');
delete_option('tag_amazon_sin_api_niche');
delete_option('tag_amazon_affiliate_id');
delete_option('tag_serper_consulted_titles');
delete_option('tag_api_provider');
delete_option('tag_intro_prompt'); // Agregado
delete_option('tag_subtitles_prompt'); // Agregado
delete_option('tag_faqs_prompt'); // Agregado
delete_option('tag_question_prompt'); // Agregado
delete_option('tag_system_prompt'); // Agregado
// Eliminar el evento programado (cron)
wp_clear_scheduled_hook('tag_generate_articles_cron');
wp_clear_scheduled_hook('tag_generate_directorio_cron');
wp_clear_scheduled_hook('tag_generate_places_cron');
wp_clear_scheduled_hook('tag_generate_amazon_sin_api_cron');
}
register_uninstall_hook(__FILE__, 'tag_uninstall'); - me gustaria dividir por subpaginas mejor mi plugin, configuracion, guardado de los titulos a crear, y sub pagina de , creacion informacional, creacion de amazon sin api, creacion de directorio, creacion de reseñas individuales