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
-
-
-
-
-
Sofortiger Broadcast
-
-
-
-
-
Geplante Broadcasts (serverseitig)
-
-
-
Geplante Broadcasts verwenden automatisch die Standard-Einstellungen für Prefix und Farben.
-
-
-
-
-
-
-
-
-
Aktuelle Zeitgeplante Nachrichten
-
-
Keine geplanten Broadcasts.
-
-
- | ID | Nachricht | Sendezeit (Lokal) | Sendezeit (UTC) | Status | Wiederholung | Aktionen |
-
- $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';
- ?>
-
- |
- |
- |
- |
- |
- |
-
-
- |
-
-
-
-
-
-
-
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
+
+
+
+
+
Sofortiger Broadcast
+
+
+
+
+
Geplante Broadcasts (serverseitig)
+
+
+
Geplante Broadcasts verwenden automatisch die Standard-Einstellungen für Prefix und Farben.
+
+
+
+
+
+
+
+
+
Aktuelle Zeitgeplante Nachrichten
+
+
Keine geplanten Broadcasts.
+
+
+ | ID | Nachricht | Sendezeit (Lokal) | Sendezeit (UTC) | Status | Wiederholung | Aktionen |
+
+ $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';
+ ?>
+
+ |
+ |
+ |
+ |
+ |
+ |
+
+
+ |
+
+
+
+
+
+
+
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