上下文:我正在制作一个 WordPress 插件,它接收两个参数,一个标题和一个提示,并生成一篇应该在 120 到 180 秒之间的随机时间发布的文章。可以提出多个请求。数据保存在txt中,其中包含数据标题、提示、随机时间和记录时间。
插件部分有一个表格,显示了用 javascript 制作的倒计时,指示距离文章发布还剩多少时间,当达到 0 时就应该发布。请记住,js 中的倒计时只是视觉上的。文章的发布时间在 cron 和 get_article_y_publish() 函数中管理
理论上,crons 的执行无需用户交互。但由于某种原因,这并没有发生。
代码:
<?php
/**
* Plugin Name: Psychologist article generator
* Description: Este plugin genera un artículo apartir de un título usando la API de OpenAI.
* Version: 2.1 B
* Author: Toni cipher
* Author URI: https://cipher.icu
*/
// Ubicación del archivo
$filename = plugin_dir_path(__FILE__) . 'titulos_y_prompts.txt';
// Activación del plugin
function mi_plugin_activado() {
// Agregar el horario de cron personalizado (si no existe)
if ( ! wp_next_scheduled( 'my_custom_function' ) ) {
wp_schedule_event( time(), 'per_minute', 'my_custom_function' );
}
}
register_activation_hook( __FILE__, 'mi_plugin_activado' );
// Desactivación del plugin
function mi_plugin_desactivado() {
wp_clear_scheduled_hook( 'my_custom_function' );
}
register_deactivation_hook( __FILE__, 'mi_plugin_desactivado' );
// Función para crear la página de ajustes
function ammd_admin_menu() {
add_options_page(
'Administrar Títulos',
'Automatización Artículos',
'manage_options',
'ammd-settings',
'ammd_settings_page'
);
}
add_action('admin_menu', 'ammd_admin_menu');
// HTML para la página de ajustes
function ammd_settings_page() {
global $filename;
/*
Este codigo es para mostrar cuando es el proximo tiempo en el cual se ejecutará obtener_articulo y publicar. Descomentar en caso de querer ver esa informacion.
$next_cron = wp_next_scheduled('my_custom_function');
if ($next_cron !== false) {
// Formatear la fecha y hora de la próxima ejecución
$next_cron_formatted = date('Y-m-d H:i:s', $next_cron);
// Imprimir la próxima fecha y hora de ejecución
echo "<h1>La próxima ejecución de 'my_custom_function' está programada para: $next_cron_formatted </h1>";
} else {
echo "<h1>La tarea cron 'my_custom_function' no está programada.</h1>";
}*/
if (isset($_POST['submit'])) {
if (empty($_POST['titulo']) || empty($_POST['prompt'])) {
return; // Sale del script si uno de los campos está vacío
}
}
if (isset($_POST['titulo']) && !empty($_POST['titulo']) && isset($_POST['prompt']) && !empty($_POST['prompt'])) {
// Limpiar el título y el prompt
$titulo_clean = sanitize_text_field($_POST['titulo']);
$prompt_clean = sanitize_textarea_field($_POST['prompt']);
// Generar un valor aleatorio en horas entre 8 y 20
$valor_en_segundos = rand(120, 240); // Valor en horas
// Pasar a horas
$valor_en_horas = $valor_en_segundos;// * 3600;
// Calcular el tiempo de espera para este artículo
$tiempo_espera = $valor_en_horas;
// Obtener el tiempo actual en formato Unix (time())
$tiempo_actual = time();
// Ruta del archivo de tu script que se ejecutará periódicamente
$script_a_ejecutar = __DIR__ . '/auto-post-v2.php';
$tiempo_unix = time()+$valor_en_horas;
// Convierte el valor Unix a un formato cron
$minutos = date('i', $tiempo_unix); // Minutos (30)
$horas = date('G', $tiempo_unix); // Horas (10)
// Configura el comando de cron con los valores obtenidos
$comando_cron = "$minutos $horas * * * /usr/bin/php $script_a_ejecutar";
// Ejecutar el comando para agregar el Cron Job
shell_exec('(crontab -l ; echo "' . $comando_cron . '") | crontab -');
// Crear la línea de contenido a agregar al archivo
$nuevo_contenido = "\n" . $tiempo_espera . '||' . $tiempo_actual . '||' . str_replace("\n", ' ', $titulo_clean) . '||' . str_replace("\n", ' ', $prompt_clean) . "\n";
file_put_contents($filename, $nuevo_contenido, FILE_APPEND);
echo '<div class="updated"><p>Título, Prompt agregados con éxito!</p></div>';
}
// El código HTML para la página de ajustes
echo '<div class="wrap">';
echo '<h2>Automatizador de artículos</h2>';
echo '<form method="post" action="" autocomplete="off">';
echo '<table class="form-table">';
echo '<tr>';
echo '<th scope="row"><label for="titulo">Agregar Título</label></th>';
echo '<td><input name="titulo" type="text" id="titulo" value="" class="regular-text" required></td>';
echo '</tr>';
echo '<tr>';
echo '<th scope="row"><label for="prompt">Agregar Prompt</label></th>';
echo '<td><textarea name="prompt" id="prompt" rows="5" cols="50" required></textarea></td>';
echo '</tr>';
echo '</table>';
echo '<p class="submit"><input type="submit" name="submit" id="submit" class="button button-primary" value="Guardar Título y Prompt"></p>';
echo '</form>';
// Nueva sección para mostrar los artículos y prompts en cola
echo '<h2>Cola de Artículos y Prompts</h2>';
echo '<table class="form-table">';
echo '<thead>';
echo '<tr>';
echo '<th scope="col">Título</th>';
echo '<th scope="col">Prompt</th>';
echo '<th scope="col">Tiempo de espera</th>';
echo '</tr>';
echo '</thead>';
echo '<tbody>';
// Leer el archivo y separarlo en líneas
$lines = file($filename, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
$flag = True;
if (!empty($lines)) {
$acumulado_tiempo_espera = 0; // Inicializa el tiempo acumulado en 0
foreach ($lines as $line) {
list($horas2, $tiempo_registro, $titulo, $prompt) = explode('||', $line, 4);
// Calcula el tiempo de espera para este artículo
$tiempo_espera = $horas2 + $acumulado_tiempo_espera;
// Actualiza el tiempo acumulado
$acumulado_tiempo_espera += $horas2;
// Resta el tiempo transcurrido desde el registro
$tiempo_resta = time() - $tiempo_registro;
$tiempo_espera -= $tiempo_resta;
// Separar cada línea en título y prompt
echo '<tr>';
echo '<td>' . esc_html($titulo) . '</td>';
echo '<td>' . esc_html($prompt) . '</td>';
echo '<td><span class="countdown" data-tiempo="' . esc_attr($tiempo_espera) . '"></span></td>';
echo '</tr>';
}
}
else {
echo '<tr><td colspan="2">No hay artículos o prompts en la cola.</td></tr>';
}
echo '</tbody>';
echo '</table>';
echo '</div>'; // Cierre del div "wrap"
echo '<script>
function startCountdown(element, totalSeconds) {
var interval = setInterval(function() {
var hours = Math.floor(totalSeconds / 3600);
var minutes = Math.floor((totalSeconds % 3600) / 60);
var seconds = totalSeconds % 60;
var formattedTime = formatTime(hours, minutes, seconds);
element.textContent = formattedTime;
if (totalSeconds <= 0) {
clearInterval(interval);
element.textContent = "Cuenta regresiva finalizada.";
}
totalSeconds--;
}, 1000);
}
function formatTime(hours, minutes, seconds) {
return String(hours).padStart(2, "0") + ":" + String(minutes).padStart(2, "0") + ":" + String(seconds).padStart(2, "0");
}
var countdownElements = document.querySelectorAll(".countdown");
countdownElements.forEach(function(element) {
var totalSeconds = parseInt(element.getAttribute("data-tiempo"));
startCountdown(element, totalSeconds);
});
</script>';
}
// Al activar el plugin, crea el archivo si no existe
function ammd_activate() {
global $filename;
if(!file_exists($filename)) {
touch($filename);
}
}
register_activation_hook(__FILE__, 'ammd_activate');
function reemplazar_primera_linea_no_vacia($archivo, $nuevaLinea) {
// Leer el contenido actual del archivo
$contenido = file($archivo);
// Iterar a través de las líneas y encontrar la primera no vacía
foreach ($contenido as $indice => $linea) {
$linea = trim($linea); // Eliminar espacios en blanco al principio y al final
if (!empty($linea)) {
// Reemplazar la primera línea no vacía con la nueva línea
$contenido[$indice] = $nuevaLinea . PHP_EOL;
break; // Salir del bucle después de reemplazar la primera línea
}
}
if (!is_array($contenido)) {
// Handle the case where $contenido is not an array, possibly by initializing it as an empty array.
$contenido = [];
}
// Now you can safely use implode on $contenido
file_put_contents($archivo, implode('', $contenido));
}
function obtener_acumulado_tiempo_espera($filename) {
$lines = file($filename, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
$acumulado_tiempo_espera = 0;
if (!empty($lines)) {
foreach ($lines as $line) {
list($horas, $tiempo_registro, $titulo, $prompt) = explode('||', $line, 4);
$acumulado_tiempo_espera += $horas;
}
}
return $acumulado_tiempo_espera /** 3600*/; // Convertir a segundos
}
function obtener_articulo_y_publicar() {
global $filename;
$lines = file($filename, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
if (empty($lines)) {
// No hay artículos en la cola, detener la ejecución
return;
}
// Obtener el primer artículo de la cola
list($horas2, $tiempo_registro, $titulo, $prompt) = explode('||', $lines[0], 4);
// Calcular el tiempo de espera para este artículo
$tiempo_espera = $horas2;
// Restar el tiempo transcurrido desde el registro
$tiempo_espera -= 60;
if ($tiempo_espera <= 0) {
//Esta funcion es solo para registrar en un archivo si paso por acá el codigo. Si se usa descomentar la funcion crear_archivo_pase()
//crear_archivo_pase();
$tiempo_resta = 0;
// Obtener el artículo, título y prompt desde el archivo .txt
list($titulo, $articulo) = obtener_articulo_y_titulo();
// Si no hay título o artículo, detener la ejecución
if (empty($titulo) || empty($articulo)) {
// Detiene la ejecución si no hay títulos
return;
}
// Obtener fotos relacionadas con el título del artículo
$foto_url_inicio = obtener_foto_unsplash('marketing'); // Ajusta el término según tus necesidades
$foto_url_medio = obtener_foto_unsplash('business'); // Ajusta el término según tus necesidades
// Generar el código HTML para las fotos
$foto_html_inicio = "<!-- wp:image -->\n<figure class=\"wp-block-image\"><img src=\"$foto_url_inicio\" alt=\"\"/></figure>\n<!-- /wp:image -->";
$foto_html_medio = "<!-- wp:image -->\n<figure class=\"wp-block-image\"><img src=\"$foto_url_medio\" alt=\"\"/></figure>\n<!-- /wp:image -->";
// Dividir el artículo en dos partes
$article_parts = explode("\n", $articulo);
$halfway_point = floor(count($article_parts) / 2);
$first_half = array_slice($article_parts, 0, $halfway_point);
$second_half = array_slice($article_parts, $halfway_point);
$first_half_content = '<p>' . implode('</p><p>', $first_half) . '</p>';
$second_half_content = '<p>' . implode('</p><p>', $second_half) . '</p>';
// Combinar las fotos y el artículo en el contenido del post
$post_content = $foto_html_inicio . "\n\n" . $first_half_content . "\n\n" . $foto_html_medio . "\n\n" . $second_half_content;
// Argumentos del post
$post_args = array(
'post_title' => $titulo,
'post_content' => $post_content,
'post_status' => 'publish',
'post_author' => 1,
'post_type' => 'post'
);
// Insertar el post
$id_post = wp_insert_post($post_args);
asignar_imagen_destacada($id_post, $foto_url_inicio);
// Después de publicar, eliminar el artículo de la cola
$lines = file($filename, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
if (!empty($lines)) {
unset($lines[0]);
file_put_contents($filename, implode(PHP_EOL, $lines));
}
}
else{
$linea_de_texto = implode('||', [$tiempo_espera, time(), $titulo, $prompt]);
reemplazar_primera_linea_no_vacia(__DIR__.'/titulos_y_prompts.txt',$linea_de_texto);
}
}
// Agregar el horario de cron personalizado
add_filter( 'cron_schedules', function ( $schedules ) {
$schedules['every_minute'] = array(
'interval' => 60,
'display' => __( 'Every Minute' )
);
return $schedules;
} );
// Programar la función que se ejecutará cada minuto
add_action( 'my_custom_function', 'obtener_articulo_y_publicar' );
// Programar la tarea cron
if ( ! wp_next_scheduled( 'my_custom_function' ) ) {
wp_schedule_event( time(), 'every_minute', 'my_custom_function' );
}
?>
WP_Cron,正如评论者所指出的,只能在 WordPress 收到请求时运行。请求可以来自
https://example.com/wp-cron.php?doing_wp_cron
(当然使用您的主机名而不是example.com
)。因此,您可以让 Javascript 倒计时代码在倒计时到期时对 /wp-cron.php?doing_wp_cron
执行
fetch()操作。你的 Javascript 中有这样的东西,没有调试!.
function startCountdown(element, totalSeconds) {
var interval = setInterval(function() {
var hours = Math.floor(totalSeconds / 3600);
var minutes = Math.floor((totalSeconds % 3600) / 60);
var seconds = totalSeconds % 60;
var formattedTime = formatTime(hours, minutes, seconds);
element.textContent = formattedTime;
if (totalSeconds <= 0) {
clearInterval(interval);
element.textContent = "Cuenta regresiva finalizada.";
fetch('/wp-cron.php?doing_wp_cron')
.then ( response => return response.text())
.then ();
}
totalSeconds--;
}, 1000);
}
fetch()
序列不会返回任何有用的数据,但它确实为 WP_Cron 提供了工作所需的动力。
小心!过于频繁地激活 cron 会降低站点性能。而且,这个东西很难调试。
话虽如此,最好避免依赖实时 WP_Cron 操作。