/*
Plugin Name: CIBERSEO Webpushr Automator
Description: Si quieres que Colocho no se vuelva a equivocar, este plugin deberás instalar.
Version: 1.3
Author: Daniel Niño Miralles
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
class Ciberseo_Webpushr_Automator {
private static $instance;
private $opts;
private function __construct() {
$this->opts = get_option( 'csa_settings', [] );
add_action( 'admin_menu', [ $this, 'add_admin_menu' ] );
add_action( 'admin_init', [ $this, 'settings_init' ] );
add_action( 'transition_post_status', [ $this, 'on_transition' ], 10, 3 );
add_action( 'wp_dashboard_setup', [ $this, 'register_dashboard_widget' ] );
register_activation_hook( __FILE__, [ $this, 'create_log_table' ] );
}
public static function instance() {
if ( ! self::$instance ) {
self::$instance = new self();
}
return self::$instance;
}
public function create_log_table() {
global $wpdb;
$table_name = $wpdb->prefix . 'csa_push_logs';
$charset_collate = $wpdb->get_charset_collate();
$sql = "CREATE TABLE $table_name (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
post_id BIGINT UNSIGNED NOT NULL,
post_title TEXT NOT NULL,
status VARCHAR(20) NOT NULL,
response TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
) $charset_collate;";
require_once ABSPATH . 'wp-admin/includes/upgrade.php';
dbDelta( $sql );
}
/*** 1) AJUSTES ***/
public function add_admin_menu() {
add_options_page(
'Webpushr Automator',
'Webpushr Automator',
'manage_options',
'csa_webpushr',
[ $this, 'options_page' ]
);
}
public function settings_init() {
register_setting( 'csa_webpushr', 'csa_settings', [ $this, 'sanitize' ] );
add_settings_section(
'csa_section',
'Credenciales y Ajustes',
fn() => print '
Introduce tus claves de OpenAI y Webpushr, selecciona modelo, prompt y retraso de envío. Creado por Daniel Niño Miralles de Ciberseo Jaén
',
'csa_webpushr'
);
$fields = [
'openai_api_key' => ['OpenAI API Key', [ $this, 'field_openai_key' ]],
'openai_model' => ['OpenAI Model', [ $this, 'field_openai_model' ]],
'webpushr_api_key' => ['Webpushr API Key', [ $this, 'field_webpushr_key' ]],
'webpushr_api_secret' => ['Webpushr Auth Token', [ $this, 'field_webpushr_secret' ]],
'openai_prompt' => ['Prompt OpenAI', [ $this, 'field_openai_prompt' ]],
'send_delay' => ['Retraso en minutos', [ $this, 'field_send_delay' ]],
'expire_push' => ['Expire Push', [ $this, 'field_expire_push' ]],
'auto_hide' => ['Auto Hide', [ $this, 'field_auto_hide' ]],
];
foreach ( $fields as $id => [$label, $cb] ) {
add_settings_field( $id, $label, $cb, 'csa_webpushr', 'csa_section' );
}
}
public function sanitize( $input ) {
$out = [];
$old = get_option('csa_settings', []);
$out['openai_api_key'] = sanitize_text_field( $input['openai_api_key'] ?? '' );
$out['openai_model'] = sanitize_text_field( $input['openai_model'] ?? '' );
$out['webpushr_api_key'] = sanitize_text_field( $input['webpushr_api_key'] ?? '' );
$out['webpushr_api_secret'] = sanitize_text_field( $input['webpushr_api_secret'] ?? '' );
$out['openai_prompt'] = sanitize_textarea_field( $input['openai_prompt'] ?? '' );
$out['send_delay'] = max( 1, intval( $input['send_delay'] ?? 10 ) );
$out['expire_push'] = sanitize_text_field( $input['expire_push'] ?? '1d' );
$out['auto_hide'] = ! empty( $input['auto_hide'] ) ? 1 : 0;
if ( ($old['openai_api_key'] ?? '') !== $out['openai_api_key'] ) {
delete_transient( 'csa_openai_models' );
}
return $out;
}
public function field_openai_key() {
printf(
'',
esc_attr( $this->opts['openai_api_key'] ?? '' )
);
}
public function field_openai_model() {
$current = $this->opts['openai_model'] ?? '';
$models = $this->get_openai_models();
if ( is_wp_error( $models ) ) {
echo 'Error al cargar modelos: ' . esc_html( $models->get_error_message() ) . '
';
printf(
'',
esc_attr( $current )
);
return;
}
echo '';
}
public function field_webpushr_key() {
printf(
'',
esc_attr( $this->opts['webpushr_api_key'] ?? '' )
);
}
public function field_webpushr_secret() {
printf(
'',
esc_attr( $this->opts['webpushr_api_secret'] ?? '' )
);
}
public function field_openai_prompt() {
$prompt = $this->opts['openai_prompt']
?? "Genera únicamente un objeto JSON con claves \"title\" y \"message\" para:\nTítulo: {title}\nExtracto: {excerpt}";
printf(
'',
esc_textarea( $prompt )
);
echo 'Usa {title}
y {excerpt}
como marcadores.
';
}
public function field_send_delay() {
printf(
' minutos',
intval( $this->opts['send_delay'] ?? 10 )
);
}
public function field_expire_push() {
printf(
'',
esc_attr( $this->opts['expire_push'] ?? '1d' )
);
}
public function field_auto_hide() {
$chk = ! empty( $this->opts['auto_hide'] ) ? 'checked' : '';
printf(
'',
$chk
);
}
public function options_page() {
echo '';
}
/*** 2) ENVÍO AL PUBLICAR POST ***/
public function on_transition( $new_status, $old_status, $post ) {
if ( $post->post_type !== 'post' || $old_status === 'publish' || $new_status !== 'publish' ) {
return;
}
$this->trigger_webhook( $post->ID );
}
private function trigger_webhook( int $post_id ) {
$title = get_the_title( $post_id );
$excerpt = get_the_excerpt( $post_id );
$link = get_permalink( $post_id );
$ia = $this->call_openai( $title, $excerpt );
$notif_title = $ia['title'] ?? '¡Nuevo Artículo Publicado!';
$notif_message = $ia['message'] ?? $title;
$delay = $this->opts['send_delay'] ?? 10;
$tz = new \DateTimeZone( 'Europe/Madrid' );
$dt = new \DateTimeImmutable( 'now', $tz );
$dt = $dt->add( new \DateInterval("PT{$delay}M") );
$send_at = $dt->format( 'Y-m-d H:i:s P' );
$payload = [
'name' => $title,
'title' => $notif_title,
'message' => $notif_message,
'target_url' => $link,
'send_at' => $send_at,
'expire_push' => $this->opts['expire_push'] ?? '1d',
'auto_hide' => empty( $this->opts['auto_hide'] ) ? 0 : 1,
'ttl' => 86400,
];
$response = wp_remote_post( 'https://api.webpushr.com/v1/notification/send/all', [
'headers' => [
'webpushrKey' => $this->opts['webpushr_api_key'] ?? '',
'webpushrAuthToken' => $this->opts['webpushr_api_secret'] ?? '',
'Content-Type' => 'application/json',
],
'body' => wp_json_encode( $payload ),
'timeout' => 30,
] );
$status = is_wp_error( $response ) ? 'error' : 'sent';
$res_body = is_wp_error( $response ) ? $response->get_error_message() : wp_remote_retrieve_body( $response );
// Registro en tabla
global $wpdb;
$wpdb->insert( $wpdb->prefix . 'csa_push_logs', [
'post_id' => $post_id,
'post_title' => $title,
'status' => $status,
'response' => $res_body,
] );
// Log por si acaso
error_log( "CIBERSEO-Webpushr $status post {$post_id}: " . $res_body );
}
/*** 3) OPENAI ***/
private function call_openai( string $title, string $excerpt ): ?array {
$key = $this->opts['openai_api_key'] ?? '';
$model = $this->opts['openai_model'] ?? '';
$prompt = $this->opts['openai_prompt'] ?? '';
if ( empty( $key ) || empty( $model ) || empty( $prompt ) ) return null;
$prompt = str_replace(
['{title}', '{excerpt}'],
[ $title, $excerpt ],
$prompt
);
$res = wp_remote_post( 'https://api.openai.com/v1/chat/completions', [
'headers' => [
'Content-Type' => 'application/json',
'Authorization' => 'Bearer ' . $key,
],
'body' => wp_json_encode([
'model' => $model,
'messages' => [
['role' => 'system', 'content' => 'Eres experto en copy para notificaciones push.'],
['role' => 'user', 'content' => $prompt],
],
'max_tokens' => 200,
'temperature' => 0.0,
]),
'timeout' => 30,
] );
if ( is_wp_error( $res ) ) return null;
$body = json_decode( wp_remote_retrieve_body( $res ), true );
$content = $body['choices'][0]['message']['content'] ?? '';
if ( ! preg_match( '/\{.*\}/s', $content, $m ) ) return null;
$json = json_decode( $m[0], true );
return isset( $json['title'], $json['message'] ) ? $json : null;
}
/*** 4) MODEL LIST ***/
private function get_openai_models(): array|\WP_Error {
if ( $cached = get_transient( 'csa_openai_models' ) ) return $cached;
$key = $this->opts['openai_api_key'] ?? '';
if ( ! $key ) return new \WP_Error( 'no_key', 'Falta OpenAI API Key' );
$res = wp_remote_get( 'https://api.openai.com/v1/models', [
'headers' => [ 'Authorization' => 'Bearer ' . $key ],
'timeout' => 20,
] );
if ( is_wp_error( $res ) ) return $res;
$body = json_decode( wp_remote_retrieve_body( $res ), true );
if ( empty( $body['data'] ) ) return new \WP_Error( 'bad_resp', 'Respuesta inesperada de OpenAI' );
$list = wp_list_pluck( $body['data'], 'id' );
set_transient( 'csa_openai_models', $list, 12 * HOUR_IN_SECONDS );
return $list;
}
/*** 5) WIDGET DASHBOARD ***/
public function register_dashboard_widget() {
wp_add_dashboard_widget(
'csa_push_status_widget',
'Estado de Notificaciones Push',
[ $this, 'render_push_status_widget' ]
);
}
public function render_push_status_widget() {
global $wpdb;
$table = $wpdb->prefix . 'csa_push_logs';
$rows = $wpdb->get_results( "SELECT * FROM $table ORDER BY created_at DESC LIMIT 10" );
if ( ! $rows ) {
echo 'No hay registros recientes.
';
return;
}
echo 'ID | Post | Estado | Fecha |
';
foreach ( $rows as $row ) {
printf(
'%d | %s | %s | %s |
',
$row->id,
get_edit_post_link( $row->post_id ),
esc_html( $row->post_title ),
esc_html( strtoupper( $row->status ) ),
esc_html( $row->created_at )
);
}
echo '
';
}
}
Ciberseo_Webpushr_Automator::instance();