38550-vm/settings.php
2026-02-18 05:42:57 +00:00

284 lines
14 KiB
PHP

<?php
declare(strict_types=1);
@ini_set('display_errors', '1');
@error_reporting(E_ALL);
@date_default_timezone_set('UTC');
require_once __DIR__ . '/includes/app.php';
ensure_tables();
$settings = get_settings();
$errors = [];
$saved = false;
if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') === 'save_settings') {
$prefix = $_POST['prefix'] ?? '!';
$max_volume = (int)($_POST['max_volume'] ?? 100);
$auto_reconnect = isset($_POST['auto_reconnect']) ? 1 : 0;
$log_level = $_POST['log_level'] ?? 'info';
$discord_token = $_POST['discord_token'] ?? '';
$discord_app_id = $_POST['discord_app_id'] ?? '';
$discord_public_key = $_POST['discord_public_key'] ?? '';
$pdo = db();
$stmt = $pdo->prepare('UPDATE bot_settings SET
prefix = :prefix,
max_volume = :max_volume,
auto_reconnect = :auto_reconnect,
log_level = :log_level,
discord_token = :discord_token,
discord_app_id = :discord_app_id,
discord_public_key = :discord_public_key
WHERE id = :id');
$stmt->execute([
':prefix' => $prefix,
':max_volume' => $max_volume,
':auto_reconnect' => $auto_reconnect,
':log_level' => $log_level,
':discord_token' => $discord_token,
':discord_app_id' => $discord_app_id,
':discord_public_key' => $discord_public_key,
':id' => $settings['id'],
]);
add_log('Admin', 'Settings Update', 'Bot settings were updated.');
// If credentials changed, try to auto-sync slash commands
if ($discord_token !== $settings['discord_token'] || $discord_app_id !== $settings['discord_app_id']) {
require_once __DIR__ . '/bot/register_commands.php';
$syncResult = register_commands();
add_log('System', 'Auto-Sync Commands', $syncResult);
}
$settings = get_settings(); // Refresh
$saved = true;
}
if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') === 'restart_bot') {
// Kill existing processes
shell_exec("pkill -f 'bot/worker.php'");
// Start new one
shell_exec("nohup php " . __DIR__ . "/bot/worker.php > " . __DIR__ . "/bot/bot.log 2>&1 &");
add_log('Admin', 'Worker Restart', 'The bot worker process was manually restarted from settings.');
$saved = true; // Trigger toast
}
if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') === 'sync_commands') {
require_once __DIR__ . '/bot/register_commands.php';
$syncResult = register_commands();
add_log('Admin', 'Manual Command Sync', $syncResult);
$saved = true;
}
$projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? '';
$projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
$projectName = $_SERVER['PROJECT_NAME'] ?? 'Discord Music Bot Control Center';
?>
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title><?= h($projectName) ?> — Settings</title>
<?php if ($projectDescription): ?>
<meta name="description" content="<?= h($projectDescription) ?>" />
<meta property="og:description" content="<?= h($projectDescription) ?>" />
<meta property="twitter:description" content="<?= h($projectDescription) ?>" />
<?php endif; ?>
<?php if ($projectImageUrl): ?>
<meta property="og:image" content="<?= h($projectImageUrl) ?>" />
<meta property="twitter:image" content="<?= h($projectImageUrl) ?>" />
<?php endif; ?>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
<link rel="stylesheet" href="assets/css/custom.css?v=<?= (int)time() ?>">
</head>
<body>
<nav class="navbar navbar-expand-lg sticky-top app-navbar">
<div class="container">
<a class="navbar-brand" href="index.php">EchoLift</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navMenu" aria-controls="navMenu" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navMenu">
<div class="navbar-nav ms-auto">
<a class="nav-link" href="index.php">Dashboard</a>
<a class="nav-link" href="requests.php">Queue</a>
<a class="nav-link" href="logs.php">Logs</a>
<a class="nav-link active" href="settings.php">Settings</a>
</div>
</div>
</div>
</nav>
<main class="app-shell">
<section class="container py-4 py-lg-5">
<div class="row g-4">
<div class="col-lg-7">
<p class="eyebrow">Administration</p>
<h1 class="h3">Bot policy settings</h1>
<p class="text-muted">Define default controls for who can use music commands and how playback is handled.</p>
<?php if ($errors): ?>
<div class="alert alert-warning mt-4" role="alert">
<?= h(implode(' ', $errors)) ?>
</div>
<?php endif; ?>
<div class="card app-card mt-4">
<div class="card-body">
<h2 class="h5 mb-3">Bot Credentials</h2>
<p class="small text-muted mb-4">Manage your Discord application keys. Keep these private.</p>
<form method="post" action="settings.php" class="row g-3">
<input type="hidden" name="action" value="save_settings">
<!-- Preserve existing values in hidden fields if they are not in this form,
but here we are updating the whole row, so better to include all fields in one form or handle partially.
Actually, I'll merge the forms into one for simplicity. -->
<div class="col-12">
<label class="form-label" for="discord_token">Bot Token</label>
<input type="password" class="form-control" id="discord_token" name="discord_token" value="<?= h((string)$settings['discord_token']) ?>" placeholder="MTAyNDU...">
<div class="form-text">Found in Discord Developer Portal > Bot > Token.</div>
</div>
<div class="col-md-6">
<label class="form-label" for="discord_app_id">Application ID</label>
<input class="form-control" id="discord_app_id" name="discord_app_id" value="<?= h((string)$settings['discord_app_id']) ?>" placeholder="1024...">
</div>
<div class="col-md-6">
<label class="form-label" for="discord_public_key">Public Key</label>
<input class="form-control" id="discord_public_key" name="discord_public_key" value="<?= h((string)$settings['discord_public_key']) ?>" placeholder="a1b2c3...">
</div>
<div class="col-12 mt-4 p-3 bg-light rounded border">
<h6 class="mb-2">Slash Commands Setup</h6>
<p class="small text-muted mb-2">To use slash commands like <code>/play</code>, copy this URL into your <strong>Discord Developer Portal > General Information > Interactions Endpoint URL</strong>:</p>
<div class="input-group input-group-sm">
<?php
$protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? "https://" : "http://";
$host = $_SERVER['HTTP_HOST'] ?? 'localhost';
$interactionUrl = $protocol . $host . "/api/interactions.php";
?>
<input type="text" class="form-control bg-white" value="<?= h($interactionUrl) ?>" readonly>
<button class="btn btn-outline-secondary" type="button" onclick="navigator.clipboard.writeText('<?= h($interactionUrl) ?>')">Copy</button>
</div>
<div class="form-text mt-2 text-primary">Note: Make sure your Public Key is saved above before Discord can verify this URL.</div>
</div>
<hr class="my-4">
<h2 class="h5 mb-3">Behavior Settings</h2>
<div class="col-md-6">
<label class="form-label" for="prefix">Command prefix</label>
<input class="form-control" id="prefix" name="prefix" value="<?= h($settings['prefix']) ?>" required>
</div>
<div class="col-md-6">
<label class="form-label" for="max_volume">Max volume (%)</label>
<input class="form-control" id="max_volume" name="max_volume" type="number" min="1" max="200" value="<?= h((string)$settings['max_volume']) ?>" required>
</div>
<div class="col-md-6">
<label class="form-label" for="log_level">Log level</label>
<select class="form-select" id="log_level" name="log_level">
<?php foreach (['debug', 'info', 'warn', 'error'] as $level): ?>
<option value="<?= h($level) ?>" <?= $settings['log_level'] === $level ? 'selected' : '' ?>><?= h(ucfirst($level)) ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="col-12">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="auto_reconnect" name="auto_reconnect" <?= $settings['auto_reconnect'] ? 'checked' : '' ?>>
<label class="form-check-label" for="auto_reconnect">Enable auto-reconnect when voice drops</label>
</div>
</div>
<div class="col-12 d-flex gap-2">
<button class="btn btn-primary" type="submit">Save all settings</button>
<a class="btn btn-outline-secondary" href="index.php">Back to dashboard</a>
</div>
</form>
<div class="mt-4 p-3 bg-light rounded border">
<h6 class="mb-2">Slash Commands Sync</h6>
<p class="small text-muted mb-2">If you've updated your Bot Token or App ID, you may need to manually refresh the commands on Discord.</p>
<form method="post" action="settings.php">
<input type="hidden" name="action" value="sync_commands">
<button type="submit" class="btn btn-sm btn-outline-primary">🔄 Sync Commands with Discord Now</button>
</form>
</div>
</div>
</div>
<div class="card app-card mt-4 border-danger-subtle">
<div class="card-body">
<h2 class="h5 text-danger mb-3">Process Control</h2>
<p class="small text-muted mb-4">Manage the background bot worker. Use this if the bot stops responding or after updating sensitive settings.</p>
<?php
$workerPid = shell_exec("pgrep -f 'bot/worker.php'");
$isWorkerRunning = !empty($workerPid);
?>
<div class="d-flex align-items-center justify-content-between">
<div>
<span class="badge <?= $isWorkerRunning ? 'bg-success' : 'bg-danger' ?> mb-1">
<?= $isWorkerRunning ? 'Running (PID: ' . trim($workerPid) . ')' : 'Stopped' ?>
</span>
<p class="mb-0 small text-muted">The worker process handles the queue and connections.</p>
</div>
<form method="post" action="settings.php">
<input type="hidden" name="action" value="restart_bot">
<button type="submit" class="btn btn-danger">
<?= $isWorkerRunning ? 'Restart Bot Worker' : 'Start Bot Worker' ?>
</button>
</form>
</div>
</div>
</div>
</div>
<div class="col-lg-5">
<div class="card app-card">
<div class="card-body">
<h2 class="h6">Policy preview</h2>
<div class="detail-meta mt-3">
<div>
<p class="label">Prefix</p>
<p class="value"><?= h($settings['prefix']) ?></p>
</div>
<div>
<p class="label">Max volume</p>
<p class="value"><?= h((string)$settings['max_volume']) ?>%</p>
</div>
<div>
<p class="label">Auto-reconnect</p>
<p class="value"><?= $settings['auto_reconnect'] ? 'Enabled' : 'Disabled' ?></p>
</div>
</div>
<p class="small text-muted mt-3">These defaults will be used by the bot runtime service.</p>
</div>
</div>
<div class="card app-card mt-4">
<div class="card-body">
<h2 class="h6">Stability checklist</h2>
<ul class="small text-muted mb-0">
<li>Auto-reconnect enabled for voice sessions.</li>
<li>Log level set to capture errors.</li>
<li>Volume limits keep playback stable.</li>
</ul>
</div>
</div>
</div>
</div>
</section>
</main>
<div class="toast-container position-fixed bottom-0 end-0 p-3">
<?php if ($saved): ?>
<div class="toast" role="alert" aria-live="assertive" aria-atomic="true">
<div class="toast-body">Settings saved successfully.</div>
</div>
<?php endif; ?>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
<script src="assets/js/main.js?v=<?= (int)time() ?>"></script>
</body>
</html>