/* 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 '
'; settings_fields( 'csa_webpushr' ); do_settings_sections( 'csa_webpushr' ); submit_button(); 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 ''; foreach ( $rows as $row ) { printf( '', $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 '
IDPostEstadoFecha
%d%s%s%s
'; } } Ciberseo_Webpushr_Automator::instance();