'',
'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();