From 15d702f37661374e7dff6af66ef53f50f7d0f610 Mon Sep 17 00:00:00 2001 From: M_Viper Date: Sat, 17 Jan 2026 18:22:18 +0000 Subject: [PATCH] pulsecast.php aktualisiert --- pulsecast.php | 1205 ++++++++++++++++++++++++------------------------- 1 file changed, 602 insertions(+), 603 deletions(-) diff --git a/pulsecast.php b/pulsecast.php index 2992eba..57fa575 100644 --- a/pulsecast.php +++ b/pulsecast.php @@ -1,604 +1,603 @@ - '', - 'api_key' => '', - 'broadcast_prefix' => '[Broadcast]', - 'broadcast_prefix_color' => '&c', - 'broadcast_bracket_color' => '&8', // Neu: Standard Dunkelgrau - 'broadcast_message_color' => '&f', - ]; - add_option(self::OPTION_KEY, $defaults); - } - if (!get_option(self::SCHEDULES_KEY)) { - add_option(self::SCHEDULES_KEY, []); - } - } - - public function deactivate() { - $schedules = get_option(self::SCHEDULES_KEY, []); - foreach ($schedules as $id => $s) { - $next = wp_next_scheduled(self::CRON_HOOK, [$id]); - if ($next !== false) { - wp_unschedule_event($next, self::CRON_HOOK, [$id]); - } - } - } - - public function add_weekly_cron($s) { - if (!isset($s['weekly'])) { - $s['weekly'] = ['interval' => 7*24*60*60, 'display' => __('Weekly')]; - } - return $s; - } - - public function admin_menu() { - add_menu_page('PulseCast', 'PulseCast', 'manage_options', 'pulsecast', [$this, 'page_main'], 'dashicons-megaphone', 55); - } - - public function page_main() { - if (!current_user_can('manage_options')) wp_die('Keine Berechtigung.'); - - $settings = get_option(self::OPTION_KEY, []); - $schedules = get_option(self::SCHEDULES_KEY, []); - - // Feedback Nachrichten - if (isset($_GET['pulsecast_status'])) { - echo '

' . esc_html($_GET['pulsecast_status']) . '

'; - } - if (isset($_GET['pulsecast_error'])) { - echo '

' . esc_html($_GET['pulsecast_error']) . '

'; - } - - $current_server_time = current_time('Y-m-d H:i:s'); - $current_utc_time = gmdate('Y-m-d H:i:s'); - ?> -
-

PulseCast — Broadcasts

- -
-

Aktuelle Zeit (Server-Zeitzone):

-

Aktuelle Zeit (UTC):

-

Zeitgeplante Broadcasts werden in UTC an die StatusAPI gesendet.

-
- -

Einstellungen

-
- - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -

Gib die Serveradresse an — Port (9191) und Pfad (/broadcast) werden automatisch ergänzt.

-
- -

Beispiel: Broadcast oder [Broadcast]. Die Klammern werden bei Bedarf automatisch mit der Klammer-Farbe eingefärbt.

-
- -

Farbe für den Text INNENHALB der Klammern (z. B. &c Rot).

-
- -

Farbe für die Klammern [ ] (z. B. &8 Dunkelgrau). Leer lassen für Standardfarbe.

-
- -

Farbcodes mit & (z. B. &f).

-
-

-
- -
- -

Sofortiger Broadcast

-
- - - - - - - -
-

-
- -
- -

Geplante Broadcasts (serverseitig)

-
-
-

Geplante Broadcasts verwenden automatisch die Standard-Einstellungen für Prefix und Farben.

-
-
-
- - - -
-
-
- -
- - - - - - - - - - - - - - - -
- -

Beispiel: (in einer Stunde)

-
- -
-

-
- -

Aktuelle Zeitgeplante Nachrichten

- -

Keine geplanten Broadcasts.

- - - - - $s): - $local_time = get_date_from_gmt(gmdate('Y-m-d H:i:s', $s['time']), 'Y-m-d H:i:s'); - $utc_time = gmdate('Y-m-d H:i:s', $s['time']); - $now = time(); - $is_past = $s['time'] <= $now; - $status = $is_past ? '⚠️ Verpasst/Verarbeitet' : '⏰ Geplant'; - ?> - - - - - - - - - - - -
IDNachrichtSendezeit (Lokal)Sendezeit (UTC)StatusWiederholungAktionen
-
- - - - -
-
- - -

Hinweis: Alle Broadcasts werden global gesendet.

-
- send_to_api($message, $type, $prefix, $prefix_color, $bracket_color, $message_color); - $code = is_wp_error($res) ? 'error' : 'ok'; - wp_redirect(add_query_arg('pulsecast_status', $code, wp_get_referer())); - exit; - } - - public function handle_schedule_post() { - if (!current_user_can('manage_options')) wp_die('Forbidden'); - - if (isset($_POST['_wpnonce']) && wp_verify_nonce($_POST['_wpnonce'], 'pulsecast_save_settings')) { - check_admin_referer('pulsecast_save_settings'); - $settings = get_option(self::OPTION_KEY, []); - $settings['api_url'] = esc_url_raw($_POST['api_url'] ?? ''); - $settings['api_key'] = sanitize_text_field($_POST['api_key'] ?? ''); - $settings['broadcast_prefix'] = sanitize_text_field($_POST['broadcast_prefix'] ?? '[Broadcast]'); - $settings['broadcast_prefix_color'] = sanitize_text_field($_POST['broadcast_prefix_color'] ?? '&c'); - $settings['broadcast_bracket_color'] = sanitize_text_field($_POST['broadcast_bracket_color'] ?? '&8'); // Neu - $settings['broadcast_message_color'] = sanitize_text_field($_POST['broadcast_message_color'] ?? '&f'); - update_option(self::OPTION_KEY, $settings); - wp_redirect(add_query_arg('pulsecast_status', 'settings_saved', wp_get_referer())); - exit; - } - - check_admin_referer('pulsecast_schedule_new'); - $message = sanitize_textarea_field($_POST['sched_message'] ?? ''); - $timeRaw = sanitize_text_field($_POST['sched_time'] ?? ''); - $recur = sanitize_text_field($_POST['sched_recur'] ?? 'none'); - - $type = 'global'; - - $settings = get_option(self::OPTION_KEY, []); - - $prefix = sanitize_text_field($_POST['sched_prefix'] ?? ''); - $prefix_color = sanitize_text_field($_POST['sched_prefix_color'] ?? ''); - $bracket_color = sanitize_text_field($_POST['sched_bracket_color'] ?? ''); // Neu - $message_color = sanitize_text_field($_POST['sched_message_color'] ?? ''); - - if (empty($prefix)) $prefix = $settings['broadcast_prefix'] ?? '[Broadcast]'; - if (empty($prefix_color)) $prefix_color = $settings['broadcast_prefix_color'] ?? '&c'; - if (empty($bracket_color)) $bracket_color = $settings['broadcast_bracket_color'] ?? '&8'; // Neu - if (empty($message_color)) $message_color = $settings['broadcast_message_color'] ?? '&f'; - - if (empty($message) || empty($timeRaw)) { - wp_redirect(add_query_arg('pulsecast_error', 'empty_fields', wp_get_referer())); - exit; - } - - $timeRaw = str_replace('T', ' ', $timeRaw); - $local_timestamp = strtotime($timeRaw); - if ($local_timestamp === false || $local_timestamp <= 0) { - wp_redirect(add_query_arg('pulsecast_error', 'bad_time', wp_get_referer())); - exit; - } - - $utc_datetime = get_gmt_from_date(date('Y-m-d H:i:s', $local_timestamp), 'Y-m-d H:i:s'); - $timestamp_utc = strtotime($utc_datetime . ' UTC'); - - $schedules = get_option(self::SCHEDULES_KEY, []); - $id = (string) time() . rand(1000,9999); - $schedules[$id] = [ - 'message' => $message, - 'time' => $timestamp_utc, - 'recur' => $recur, - 'type' => $type, - 'prefix' => $prefix, - 'prefix_color' => $prefix_color, - 'bracket_color' => $bracket_color, // Neu - 'message_color' => $message_color, - ]; - update_option(self::SCHEDULES_KEY, $schedules); - - $sent = $this->send_schedule_to_api($id, $schedules[$id]); - if (is_wp_error($sent)) { - wp_redirect(add_query_arg('pulsecast_error', 'register_failed', wp_get_referer())); - } else { - wp_redirect(add_query_arg('pulsecast_status', 'scheduled_server', wp_get_referer())); - } - exit; - } - - public function handle_resync_post() { - if (!current_user_can('manage_options')) wp_die('Forbidden'); - check_admin_referer('pulsecast_resync_action'); - - $schedules = get_option(self::SCHEDULES_KEY, []); - $count = 0; - $errors = 0; - - foreach ($schedules as $id => $s) { - $res = $this->send_schedule_to_api($id, $s); - if (is_wp_error($res)) { - $errors++; - } else { - $count++; - } - } - - $msg = "$count Broadcasts synchronisiert."; - if ($errors > 0) { - $msg .= " $errors Fehler sind aufgetreten."; - } - - wp_redirect(add_query_arg('pulsecast_status', $msg, wp_get_referer())); - exit; - } - - public function handle_delete_schedule() { - if (!current_user_can('manage_options')) wp_die('Forbidden'); - $id = sanitize_text_field($_POST['id'] ?? ''); - check_admin_referer('pulsecast_delete_schedule_'.$id); - - $schedules = get_option(self::SCHEDULES_KEY, []); - if (isset($schedules[$id])) { - $this->send_cancel_schedule_to_api($id); - unset($schedules[$id]); - update_option(self::SCHEDULES_KEY, $schedules); - } - - wp_redirect(remove_query_arg(['pulsecast_error','pulsecast_status'], wp_get_referer())); - exit; - } - - public function cron_send_broadcast($id) { - $schedules = get_option(self::SCHEDULES_KEY, []); - if (!isset($schedules[$id])) return; - - $s = $schedules[$id]; - - $res = $this->send_to_api( - $s['message'], - $s['type'] ?? 'global', - $s['prefix'] ?? '', - $s['prefix_color'] ?? '', - $s['bracket_color'] ?? '', // Neu - $s['message_color'] ?? '' - ); - - $log = get_option(self::OPTION_KEY . '_last_logs', []); - $entry = [ - 'time' => time(), - 'id' => $id, - 'result' => is_wp_error($res) ? $res->get_error_message() : 'ok' - ]; - $log[] = $entry; - if (count($log) > 50) array_shift($log); - update_option(self::OPTION_KEY . '_last_logs', $log); - - if (($s['recur'] ?? 'none') !== 'none') { - $next = $this->compute_next_timestamp($s['time'], $s['recur']); - if ($next !== null) { - $schedules[$id]['time'] = $next; - update_option(self::SCHEDULES_KEY, $schedules); - } - } else { - unset($schedules[$id]); - update_option(self::SCHEDULES_KEY, $schedules); - } - } - - private function compute_next_timestamp($currentTimestamp, $recur) { - switch ($recur) { - case 'hourly': return $currentTimestamp + HOUR_IN_SECONDS; - case 'daily': return $currentTimestamp + DAY_IN_SECONDS; - case 'weekly': return $currentTimestamp + WEEK_IN_SECONDS; - default: return null; - } - } - - private function build_final_api_url($raw_url) { - $raw = trim($raw_url); - if ($raw === '') return ''; - - if (!preg_match('#^https?://#i', $raw)) { - $raw = 'http://' . $raw; - } - - $parts = parse_url($raw); - if ($parts === false || empty($parts['host'])) return ''; - - $scheme = $parts['scheme'] ?? 'http'; - $host = $parts['host']; - $port = $parts['port'] ?? 9191; - $path = $parts['path'] ?? '/broadcast'; - if (substr($path, -1) === '/') $path = rtrim($path, '/'); - if ($path === '') $path = '/broadcast'; - $url = $scheme . '://' . $host . ($port ? ':' . $port : '') . $path; - return $url; - } - - private function send_to_api($message, $type = 'global', $prefix_override = '', $prefix_color = '', $bracket_color = '', $message_color = '') { - $settings = get_option(self::OPTION_KEY, []); - $raw = rtrim($settings['api_url'] ?? '', '/'); - $api_key = $settings['api_key'] ?? ''; - $default_prefix = $settings['broadcast_prefix'] ?? ''; - $default_prefix_color = $settings['broadcast_prefix_color'] ?? '&c'; - $default_bracket_color = $settings['broadcast_bracket_color'] ?? '&8'; // Neu - $default_message_color = $settings['broadcast_message_color'] ?? '&f'; - - if (empty($raw)) { - return new WP_Error('no_url', 'StatusAPI URL ist nicht konfiguriert.'); - } - - $final_url = $this->build_final_api_url($raw); - if (empty($final_url)) { - return new WP_Error('bad_url', 'StatusAPI URL ist ungültig.'); - } - - $payload_prefix = ($prefix_override !== '' ? $prefix_override : $default_prefix); - $payload_prefix_color = ($prefix_color !== '' ? $prefix_color : $default_prefix_color); - $payload_bracket_color = ($bracket_color !== '' ? $bracket_color : $default_bracket_color); // Neu - $payload_message_color = ($message_color !== '' ? $message_color : $default_message_color); - - $payload = [ - 'message' => $message, - 'type' => $type, - 'prefix' => $payload_prefix, - 'prefixColor' => $payload_prefix_color, - 'bracketColor' => $payload_bracket_color, // Neu - 'messageColor' => $payload_message_color, - 'meta' => [ - 'source' => 'PulseCast-WordPress', - 'time' => gmdate('c'), - ], - ]; - - $args = [ - 'body' => wp_json_encode($payload), - 'headers' => [ - 'Content-Type' => 'application/json', - ], - 'timeout' => 15, - ]; - - if (!empty($api_key)) { - $args['headers']['X-Api-Key'] = $api_key; - } - - $response = wp_remote_post($final_url, $args); - - if (is_wp_error($response)) { - return $response; - } - - $code = wp_remote_retrieve_response_code($response); - if ($code < 200 || $code >= 300) { - return new WP_Error('http_error', 'HTTP ' . $code . ': ' . wp_remote_retrieve_response_message($response)); - } - - return true; - } - - private function send_schedule_to_api($localId, $schedule) { - $settings = get_option(self::OPTION_KEY, []); - $raw = rtrim($settings['api_url'] ?? '', '/'); - $api_key = $settings['api_key'] ?? ''; - - if (empty($raw)) { - return new WP_Error('no_url', 'StatusAPI URL ist nicht konfiguriert.'); - } - $final_url = $this->build_final_api_url($raw); - if (empty($final_url)) { - return new WP_Error('bad_url', 'StatusAPI URL ist ungültig.'); - } - - $timeSec = intval($schedule['time'] ?? 0); - $timeMs = $timeSec * 1000; - - $payload = [ - 'message' => $schedule['message'] ?? '', - 'type' => 'global', - 'prefix' => $schedule['prefix'] ?? '', - 'prefixColor' => $schedule['prefix_color'] ?? '', - 'bracketColor' => $schedule['bracket_color'] ?? '', // Neu - 'messageColor' => $schedule['message_color'] ?? '', - 'scheduleTime' => $timeMs, - 'recur' => $schedule['recur'] ?? 'none', - 'clientScheduleId' => $localId, - 'meta' => [ - 'source' => 'PulseCast-WordPress', - 'time' => gmdate('c'), - 'timezone' => 'UTC', - ], - ]; - - $args = [ - 'body' => wp_json_encode($payload), - 'headers' => [ - 'Content-Type' => 'application/json', - ], - 'timeout' => 15, - ]; - if (!empty($api_key)) $args['headers']['X-Api-Key'] = $api_key; - - $response = wp_remote_post($final_url, $args); - - if (is_wp_error($response)) { - return $response; - } - - $code = wp_remote_retrieve_response_code($response); - if ($code < 200 || $code >= 300) { - return new WP_Error('http_error', 'HTTP ' . $code . ': ' . wp_remote_retrieve_response_message($response)); - } - - return true; - } - - private function send_cancel_schedule_to_api($localId) { - $settings = get_option(self::OPTION_KEY, []); - $raw = rtrim($settings['api_url'] ?? '', '/'); - $api_key = $settings['api_key'] ?? ''; - - if (empty($raw)) return new WP_Error('no_url', 'no api'); - - $base = $this->build_final_api_url($raw); - if (empty($base)) return new WP_Error('bad_url', 'bad'); - $cancelUrl = rtrim($base, '/') . '/cancel'; - - $payload = [ - 'clientScheduleId' => $localId, - 'meta' => ['source' => 'PulseCast-WordPress', 'time' => gmdate('c')], - ]; - - $args = [ - 'body' => wp_json_encode($payload), - 'headers' => ['Content-Type' => 'application/json'], - 'timeout' => 10, - ]; - if (!empty($api_key)) $args['headers']['X-Api-Key'] = $api_key; - - return wp_remote_post($cancelUrl, $args); - } -} - + '', + 'api_key' => '', + 'broadcast_prefix' => '[Broadcast]', + 'broadcast_prefix_color' => '&c', + 'broadcast_bracket_color' => '&8', // Neu: Standard Dunkelgrau + 'broadcast_message_color' => '&f', + ]; + add_option(self::OPTION_KEY, $defaults); + } + if (!get_option(self::SCHEDULES_KEY)) { + add_option(self::SCHEDULES_KEY, []); + } + } + + public function deactivate() { + $schedules = get_option(self::SCHEDULES_KEY, []); + foreach ($schedules as $id => $s) { + $next = wp_next_scheduled(self::CRON_HOOK, [$id]); + if ($next !== false) { + wp_unschedule_event($next, self::CRON_HOOK, [$id]); + } + } + } + + public function add_weekly_cron($s) { + if (!isset($s['weekly'])) { + $s['weekly'] = ['interval' => 7*24*60*60, 'display' => __('Weekly')]; + } + return $s; + } + + public function admin_menu() { + add_menu_page('PulseCast', 'PulseCast', 'manage_options', 'pulsecast', [$this, 'page_main'], 'dashicons-megaphone', 55); + } + + public function page_main() { + if (!current_user_can('manage_options')) wp_die('Keine Berechtigung.'); + + $settings = get_option(self::OPTION_KEY, []); + $schedules = get_option(self::SCHEDULES_KEY, []); + + // Feedback Nachrichten + if (isset($_GET['pulsecast_status'])) { + echo '

' . esc_html($_GET['pulsecast_status']) . '

'; + } + if (isset($_GET['pulsecast_error'])) { + echo '

' . esc_html($_GET['pulsecast_error']) . '

'; + } + + $current_server_time = current_time('Y-m-d H:i:s'); + $current_utc_time = gmdate('Y-m-d H:i:s'); + ?> +
+

PulseCast — Broadcasts

+ +
+

Aktuelle Zeit (Server-Zeitzone):

+

Aktuelle Zeit (UTC):

+

Zeitgeplante Broadcasts werden in UTC an die StatusAPI gesendet.

+
+ +

Einstellungen

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +

Gib die Serveradresse an — Port (9191) und Pfad (/broadcast) werden automatisch ergänzt.

+
+ +

Beispiel: Broadcast oder [Broadcast]. Die Klammern werden bei Bedarf automatisch mit der Klammer-Farbe eingefärbt.

+
+ +

Farbe für den Text INNENHALB der Klammern (z. B. &c Rot).

+
+ +

Farbe für die Klammern [ ] (z. B. &8 Dunkelgrau). Leer lassen für Standardfarbe.

+
+ +

Farbcodes mit & (z. B. &f).

+
+

+
+ +
+ +

Sofortiger Broadcast

+
+ + + + + + + +
+

+
+ +
+ +

Geplante Broadcasts (serverseitig)

+
+
+

Geplante Broadcasts verwenden automatisch die Standard-Einstellungen für Prefix und Farben.

+
+
+
+ + + +
+
+
+ +
+ + + + + + + + + + + + + + + +
+ +

Beispiel: (in einer Stunde)

+
+ +
+

+
+ +

Aktuelle Zeitgeplante Nachrichten

+ +

Keine geplanten Broadcasts.

+ + + + + $s): + $local_time = get_date_from_gmt(gmdate('Y-m-d H:i:s', $s['time']), 'Y-m-d H:i:s'); + $utc_time = gmdate('Y-m-d H:i:s', $s['time']); + $now = time(); + $is_past = $s['time'] <= $now; + $status = $is_past ? '⚠️ Verpasst/Verarbeitet' : '⏰ Geplant'; + ?> + + + + + + + + + + + +
IDNachrichtSendezeit (Lokal)Sendezeit (UTC)StatusWiederholungAktionen
+
+ + + + +
+
+ + +

Hinweis: Alle Broadcasts werden global gesendet.

+
+ send_to_api($message, $type, $prefix, $prefix_color, $bracket_color, $message_color); + $code = is_wp_error($res) ? 'error' : 'ok'; + wp_redirect(add_query_arg('pulsecast_status', $code, wp_get_referer())); + exit; + } + + public function handle_schedule_post() { + if (!current_user_can('manage_options')) wp_die('Forbidden'); + + if (isset($_POST['_wpnonce']) && wp_verify_nonce($_POST['_wpnonce'], 'pulsecast_save_settings')) { + check_admin_referer('pulsecast_save_settings'); + $settings = get_option(self::OPTION_KEY, []); + $settings['api_url'] = esc_url_raw($_POST['api_url'] ?? ''); + $settings['api_key'] = sanitize_text_field($_POST['api_key'] ?? ''); + $settings['broadcast_prefix'] = sanitize_text_field($_POST['broadcast_prefix'] ?? '[Broadcast]'); + $settings['broadcast_prefix_color'] = sanitize_text_field($_POST['broadcast_prefix_color'] ?? '&c'); + $settings['broadcast_bracket_color'] = sanitize_text_field($_POST['broadcast_bracket_color'] ?? '&8'); // Neu + $settings['broadcast_message_color'] = sanitize_text_field($_POST['broadcast_message_color'] ?? '&f'); + update_option(self::OPTION_KEY, $settings); + wp_redirect(add_query_arg('pulsecast_status', 'settings_saved', wp_get_referer())); + exit; + } + + check_admin_referer('pulsecast_schedule_new'); + $message = sanitize_textarea_field($_POST['sched_message'] ?? ''); + $timeRaw = sanitize_text_field($_POST['sched_time'] ?? ''); + $recur = sanitize_text_field($_POST['sched_recur'] ?? 'none'); + + $type = 'global'; + + $settings = get_option(self::OPTION_KEY, []); + + $prefix = sanitize_text_field($_POST['sched_prefix'] ?? ''); + $prefix_color = sanitize_text_field($_POST['sched_prefix_color'] ?? ''); + $bracket_color = sanitize_text_field($_POST['sched_bracket_color'] ?? ''); // Neu + $message_color = sanitize_text_field($_POST['sched_message_color'] ?? ''); + + if (empty($prefix)) $prefix = $settings['broadcast_prefix'] ?? '[Broadcast]'; + if (empty($prefix_color)) $prefix_color = $settings['broadcast_prefix_color'] ?? '&c'; + if (empty($bracket_color)) $bracket_color = $settings['broadcast_bracket_color'] ?? '&8'; // Neu + if (empty($message_color)) $message_color = $settings['broadcast_message_color'] ?? '&f'; + + if (empty($message) || empty($timeRaw)) { + wp_redirect(add_query_arg('pulsecast_error', 'empty_fields', wp_get_referer())); + exit; + } + + $timeRaw = str_replace('T', ' ', $timeRaw); + $local_timestamp = strtotime($timeRaw); + if ($local_timestamp === false || $local_timestamp <= 0) { + wp_redirect(add_query_arg('pulsecast_error', 'bad_time', wp_get_referer())); + exit; + } + + $utc_datetime = get_gmt_from_date(date('Y-m-d H:i:s', $local_timestamp), 'Y-m-d H:i:s'); + $timestamp_utc = strtotime($utc_datetime . ' UTC'); + + $schedules = get_option(self::SCHEDULES_KEY, []); + $id = (string) time() . rand(1000,9999); + $schedules[$id] = [ + 'message' => $message, + 'time' => $timestamp_utc, + 'recur' => $recur, + 'type' => $type, + 'prefix' => $prefix, + 'prefix_color' => $prefix_color, + 'bracket_color' => $bracket_color, // Neu + 'message_color' => $message_color, + ]; + update_option(self::SCHEDULES_KEY, $schedules); + + $sent = $this->send_schedule_to_api($id, $schedules[$id]); + if (is_wp_error($sent)) { + wp_redirect(add_query_arg('pulsecast_error', 'register_failed', wp_get_referer())); + } else { + wp_redirect(add_query_arg('pulsecast_status', 'scheduled_server', wp_get_referer())); + } + exit; + } + + public function handle_resync_post() { + if (!current_user_can('manage_options')) wp_die('Forbidden'); + check_admin_referer('pulsecast_resync_action'); + + $schedules = get_option(self::SCHEDULES_KEY, []); + $count = 0; + $errors = 0; + + foreach ($schedules as $id => $s) { + $res = $this->send_schedule_to_api($id, $s); + if (is_wp_error($res)) { + $errors++; + } else { + $count++; + } + } + + $msg = "$count Broadcasts synchronisiert."; + if ($errors > 0) { + $msg .= " $errors Fehler sind aufgetreten."; + } + + wp_redirect(add_query_arg('pulsecast_status', $msg, wp_get_referer())); + exit; + } + + public function handle_delete_schedule() { + if (!current_user_can('manage_options')) wp_die('Forbidden'); + $id = sanitize_text_field($_POST['id'] ?? ''); + check_admin_referer('pulsecast_delete_schedule_'.$id); + + $schedules = get_option(self::SCHEDULES_KEY, []); + if (isset($schedules[$id])) { + $this->send_cancel_schedule_to_api($id); + unset($schedules[$id]); + update_option(self::SCHEDULES_KEY, $schedules); + } + + wp_redirect(remove_query_arg(['pulsecast_error','pulsecast_status'], wp_get_referer())); + exit; + } + + public function cron_send_broadcast($id) { + $schedules = get_option(self::SCHEDULES_KEY, []); + if (!isset($schedules[$id])) return; + + $s = $schedules[$id]; + + $res = $this->send_to_api( + $s['message'], + $s['type'] ?? 'global', + $s['prefix'] ?? '', + $s['prefix_color'] ?? '', + $s['bracket_color'] ?? '', // Neu + $s['message_color'] ?? '' + ); + + $log = get_option(self::OPTION_KEY . '_last_logs', []); + $entry = [ + 'time' => time(), + 'id' => $id, + 'result' => is_wp_error($res) ? $res->get_error_message() : 'ok' + ]; + $log[] = $entry; + if (count($log) > 50) array_shift($log); + update_option(self::OPTION_KEY . '_last_logs', $log); + + if (($s['recur'] ?? 'none') !== 'none') { + $next = $this->compute_next_timestamp($s['time'], $s['recur']); + if ($next !== null) { + $schedules[$id]['time'] = $next; + update_option(self::SCHEDULES_KEY, $schedules); + } + } else { + unset($schedules[$id]); + update_option(self::SCHEDULES_KEY, $schedules); + } + } + + private function compute_next_timestamp($currentTimestamp, $recur) { + switch ($recur) { + case 'hourly': return $currentTimestamp + HOUR_IN_SECONDS; + case 'daily': return $currentTimestamp + DAY_IN_SECONDS; + case 'weekly': return $currentTimestamp + WEEK_IN_SECONDS; + default: return null; + } + } + + private function build_final_api_url($raw_url) { + $raw = trim($raw_url); + if ($raw === '') return ''; + + if (!preg_match('#^https?://#i', $raw)) { + $raw = 'http://' . $raw; + } + + $parts = parse_url($raw); + if ($parts === false || empty($parts['host'])) return ''; + + $scheme = $parts['scheme'] ?? 'http'; + $host = $parts['host']; + $port = $parts['port'] ?? 9191; + $path = $parts['path'] ?? '/broadcast'; + if (substr($path, -1) === '/') $path = rtrim($path, '/'); + if ($path === '') $path = '/broadcast'; + $url = $scheme . '://' . $host . ($port ? ':' . $port : '') . $path; + return $url; + } + + private function send_to_api($message, $type = 'global', $prefix_override = '', $prefix_color = '', $bracket_color = '', $message_color = '') { + $settings = get_option(self::OPTION_KEY, []); + $raw = rtrim($settings['api_url'] ?? '', '/'); + $api_key = $settings['api_key'] ?? ''; + $default_prefix = $settings['broadcast_prefix'] ?? ''; + $default_prefix_color = $settings['broadcast_prefix_color'] ?? '&c'; + $default_bracket_color = $settings['broadcast_bracket_color'] ?? '&8'; // Neu + $default_message_color = $settings['broadcast_message_color'] ?? '&f'; + + if (empty($raw)) { + return new WP_Error('no_url', 'StatusAPI URL ist nicht konfiguriert.'); + } + + $final_url = $this->build_final_api_url($raw); + if (empty($final_url)) { + return new WP_Error('bad_url', 'StatusAPI URL ist ungültig.'); + } + + $payload_prefix = ($prefix_override !== '' ? $prefix_override : $default_prefix); + $payload_prefix_color = ($prefix_color !== '' ? $prefix_color : $default_prefix_color); + $payload_bracket_color = ($bracket_color !== '' ? $bracket_color : $default_bracket_color); // Neu + $payload_message_color = ($message_color !== '' ? $message_color : $default_message_color); + + $payload = [ + 'message' => $message, + 'type' => $type, + 'prefix' => $payload_prefix, + 'prefixColor' => $payload_prefix_color, + 'bracketColor' => $payload_bracket_color, // Neu + 'messageColor' => $payload_message_color, + 'meta' => [ + 'source' => 'PulseCast-WordPress', + 'time' => gmdate('c'), + ], + ]; + + $args = [ + 'body' => wp_json_encode($payload), + 'headers' => [ + 'Content-Type' => 'application/json', + ], + 'timeout' => 15, + ]; + + if (!empty($api_key)) { + $args['headers']['X-Api-Key'] = $api_key; + } + + $response = wp_remote_post($final_url, $args); + + if (is_wp_error($response)) { + return $response; + } + + $code = wp_remote_retrieve_response_code($response); + if ($code < 200 || $code >= 300) { + return new WP_Error('http_error', 'HTTP ' . $code . ': ' . wp_remote_retrieve_response_message($response)); + } + + return true; + } + + private function send_schedule_to_api($localId, $schedule) { + $settings = get_option(self::OPTION_KEY, []); + $raw = rtrim($settings['api_url'] ?? '', '/'); + $api_key = $settings['api_key'] ?? ''; + + if (empty($raw)) { + return new WP_Error('no_url', 'StatusAPI URL ist nicht konfiguriert.'); + } + $final_url = $this->build_final_api_url($raw); + if (empty($final_url)) { + return new WP_Error('bad_url', 'StatusAPI URL ist ungültig.'); + } + + $timeSec = intval($schedule['time'] ?? 0); + $timeMs = $timeSec * 1000; + + $payload = [ + 'message' => $schedule['message'] ?? '', + 'type' => 'global', + 'prefix' => $schedule['prefix'] ?? '', + 'prefixColor' => $schedule['prefix_color'] ?? '', + 'bracketColor' => $schedule['bracket_color'] ?? '', // Neu + 'messageColor' => $schedule['message_color'] ?? '', + 'scheduleTime' => $timeMs, + 'recur' => $schedule['recur'] ?? 'none', + 'clientScheduleId' => $localId, + 'meta' => [ + 'source' => 'PulseCast-WordPress', + 'time' => gmdate('c'), + 'timezone' => 'UTC', + ], + ]; + + $args = [ + 'body' => wp_json_encode($payload), + 'headers' => [ + 'Content-Type' => 'application/json', + ], + 'timeout' => 15, + ]; + if (!empty($api_key)) $args['headers']['X-Api-Key'] = $api_key; + + $response = wp_remote_post($final_url, $args); + + if (is_wp_error($response)) { + return $response; + } + + $code = wp_remote_retrieve_response_code($response); + if ($code < 200 || $code >= 300) { + return new WP_Error('http_error', 'HTTP ' . $code . ': ' . wp_remote_retrieve_response_message($response)); + } + + return true; + } + + private function send_cancel_schedule_to_api($localId) { + $settings = get_option(self::OPTION_KEY, []); + $raw = rtrim($settings['api_url'] ?? '', '/'); + $api_key = $settings['api_key'] ?? ''; + + if (empty($raw)) return new WP_Error('no_url', 'no api'); + + $base = $this->build_final_api_url($raw); + if (empty($base)) return new WP_Error('bad_url', 'bad'); + $cancelUrl = rtrim($base, '/') . '/cancel'; + + $payload = [ + 'clientScheduleId' => $localId, + 'meta' => ['source' => 'PulseCast-WordPress', 'time' => gmdate('c')], + ]; + + $args = [ + 'body' => wp_json_encode($payload), + 'headers' => ['Content-Type' => 'application/json'], + 'timeout' => 10, + ]; + if (!empty($api_key)) $args['headers']['X-Api-Key'] = $api_key; + + return wp_remote_post($cancelUrl, $args); + } +} + new PulseCast(); \ No newline at end of file