280 lines
11 KiB
PHP
280 lines
11 KiB
PHP
<?php
|
|
require_once __DIR__ . '/db/config.php';
|
|
|
|
function get_setting($key) {
|
|
$stmt = db()->prepare("SELECT setting_value FROM bot_settings WHERE setting_key = ?");
|
|
$stmt->execute([$key]);
|
|
return $stmt->fetchColumn() ?: '';
|
|
}
|
|
|
|
function save_setting($key, $value) {
|
|
$stmt = db()->prepare("UPDATE bot_settings SET setting_value = ? WHERE setting_key = ?");
|
|
$stmt->execute([$value, $key]);
|
|
}
|
|
|
|
$message = '';
|
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|
$token = $_POST['discord_token'] ?? '';
|
|
$guild = $_POST['guild_id'] ?? '';
|
|
$channel = $_POST['voice_channel_id'] ?? '';
|
|
$alarm = $_POST['alarm_time'] ?? '';
|
|
|
|
save_setting('discord_token', $token);
|
|
save_setting('guild_id', $guild);
|
|
save_setting('voice_channel_id', $channel);
|
|
save_setting('alarm_time', $alarm);
|
|
|
|
// Also update JSON for the bot
|
|
$config = [
|
|
'discord_token' => $token,
|
|
'guild_id' => $guild,
|
|
'voice_channel_id' => $channel,
|
|
'alarm_time' => $alarm,
|
|
'last_voice_channel' => json_decode(get_setting('last_voice_channel'), true) ?: null
|
|
];
|
|
if (!is_dir(__DIR__ . '/data')) mkdir(__DIR__ . '/data', 0775, true);
|
|
file_put_contents(__DIR__ . '/data/config.json', json_encode($config, JSON_PRETTY_PRINT));
|
|
|
|
$message = 'Configuration saved successfully!';
|
|
}
|
|
|
|
$token = get_setting('discord_token');
|
|
$guildId = get_setting('guild_id');
|
|
$voiceId = get_setting('voice_channel_id');
|
|
$alarmTime = get_setting('alarm_time');
|
|
?>
|
|
<!doctype html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<title>Discord Alarm Sahur — Dashboard</title>
|
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap" rel="stylesheet">
|
|
<style>
|
|
:root {
|
|
--primary-bg: #f8f9fa;
|
|
--surface-bg: #ffffff;
|
|
--border-color: #e5e5e5;
|
|
--text-main: #111111;
|
|
--text-muted: #666666;
|
|
--accent: #007aff;
|
|
}
|
|
body {
|
|
font-family: 'Inter', system-ui, -apple-system, sans-serif;
|
|
background-color: var(--primary-bg);
|
|
color: var(--text-main);
|
|
padding: 2rem 1rem;
|
|
}
|
|
.container { max-width: 600px; }
|
|
.card {
|
|
background: var(--surface-bg);
|
|
border: 1px solid var(--border-color);
|
|
border-radius: 4px;
|
|
box-shadow: none;
|
|
margin-bottom: 1.5rem;
|
|
}
|
|
.card-header {
|
|
background: transparent;
|
|
border-bottom: 1px solid var(--border-color);
|
|
padding: 1rem 1.25rem;
|
|
font-weight: 600;
|
|
}
|
|
.btn-primary {
|
|
background-color: var(--text-main);
|
|
border-color: var(--text-main);
|
|
border-radius: 4px;
|
|
padding: 0.5rem 1rem;
|
|
font-weight: 500;
|
|
}
|
|
.btn-primary:hover {
|
|
background-color: #333;
|
|
border-color: #333;
|
|
}
|
|
.form-control {
|
|
border-radius: 4px;
|
|
border: 1px solid var(--border-color);
|
|
padding: 0.6rem;
|
|
}
|
|
.form-control:focus {
|
|
border-color: var(--accent);
|
|
box-shadow: 0 0 0 2px rgba(0, 122, 255, 0.1);
|
|
}
|
|
.status-dot {
|
|
height: 10px;
|
|
width: 10px;
|
|
background-color: #ccc;
|
|
border-radius: 50%;
|
|
display: inline-block;
|
|
margin-right: 5px;
|
|
}
|
|
.status-online { background-color: #28a745; }
|
|
.audio-preview {
|
|
background: #f1f1f1;
|
|
padding: 10px;
|
|
border-radius: 4px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
}
|
|
h1 { font-size: 1.5rem; font-weight: 600; margin-bottom: 1.5rem; }
|
|
.hint { font-size: 0.85rem; color: var(--text-muted); margin-top: 0.25rem; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
|
|
<div class="container">
|
|
<?php
|
|
$bot_status = 'Offline';
|
|
$bot_running = false;
|
|
$pm2_output = [];
|
|
exec("sudo -u ubuntu /usr/bin/pm2 jlist", $pm2_output);
|
|
$pm2_data = json_decode(implode('', $pm2_output), true);
|
|
if ($pm2_data) {
|
|
foreach ($pm2_data as $proc) {
|
|
if ($proc['name'] === 'discord-bot') {
|
|
$bot_status = ucfirst($proc['pm2_env']['status']);
|
|
$bot_running = ($proc['pm2_env']['status'] === 'online');
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
?>
|
|
<header class="d-flex justify-content-between align-items-center mb-4">
|
|
<h1>Alarm Sahur Bot</h1>
|
|
<div class="small text-muted">
|
|
<span class="status-dot <?= $bot_running ? 'status-online' : '' ?>"></span>
|
|
Bot: <strong><?= $bot_status ?></strong>
|
|
</div>
|
|
</header>
|
|
|
|
<?php if ($message): ?>
|
|
<div class="alert alert-success py-2 px-3 small border-0 rounded-1 mb-4" role="alert">
|
|
<?= htmlspecialchars($message) ?>
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
<div class="card">
|
|
<div class="card-header">Configuration</div>
|
|
<div class="card-body">
|
|
<form method="POST">
|
|
<div class="mb-3">
|
|
<label class="form-label small fw-medium">Discord Bot Token</label>
|
|
<input type="password" name="discord_token" class="form-control" value="<?= htmlspecialchars($token) ?>" placeholder="MTAyMzQ1Njc4OTAxMjM0NTY3OA...">
|
|
<div class="hint">Obtained from Discord Developer Portal.</div>
|
|
</div>
|
|
<div class="row">
|
|
<div class="col-md-6 mb-3">
|
|
<label class="form-label small fw-medium">Guild ID</label>
|
|
<input type="text" name="guild_id" class="form-control" value="<?= htmlspecialchars($guildId) ?>" placeholder="1234567890...">
|
|
</div>
|
|
<div class="col-md-6 mb-3">
|
|
<label class="form-label small fw-medium">Voice Channel ID</label>
|
|
<input type="text" name="voice_channel_id" class="form-control" value="<?= htmlspecialchars($voiceId) ?>" placeholder="9876543210...">
|
|
</div>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label small fw-medium">Alarm Time (HH:MM)</label>
|
|
<input type="time" name="alarm_time" class="form-control" value="<?= htmlspecialchars($alarmTime) ?>">
|
|
<div class="hint">Bot will automatically join the voice channel and play audio at this time.</div>
|
|
</div>
|
|
<button type="submit" class="btn btn-primary w-100 mt-2">Save Configuration</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<div class="card-header">Bot Controls</div>
|
|
<div class="card-body">
|
|
<p class="small text-muted mb-3">Use these controls to manage the bot process. If the bot is not responding, try resetting it.</p>
|
|
<button id="resetBotBtn" class="btn btn-outline-danger w-100 fw-medium">
|
|
<span id="btnText">Reset Bot Process</span>
|
|
<span id="btnLoader" class="spinner-border spinner-border-sm d-none" role="status" aria-hidden="true"></span>
|
|
</button>
|
|
<div id="resetStatus" class="hint mt-2 text-center d-none"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<div class="card-header d-flex justify-content-between align-items-center">
|
|
<span>Audio Asset</span>
|
|
<a href="audio_assets.php" class="btn btn-sm btn-outline-primary">Manage</a>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="audio-preview">
|
|
<span class="small fw-medium">sahur.mp3</span>
|
|
<audio id="sahurPlayer" src="assets/audio/sahur.mp3?v=<?= time() ?>"></audio>
|
|
<button class="btn btn-sm btn-outline-dark" onclick="document.getElementById('sahurPlayer').play()">Preview</button>
|
|
</div>
|
|
<div class="hint mt-2">File located at <code>assets/audio/sahur.mp3</code>.</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<div class="card-header">Quick Start</div>
|
|
<div class="card-body small">
|
|
<ol class="ps-3 mb-0">
|
|
<li>Configure the Token and Channel IDs above.</li>
|
|
<li>Ensure <code>node_modules</code> are installed (run <code>npm install</code>).</li>
|
|
<li>Start the bot via <code>node index.js</code>.</li>
|
|
<li>Use <code>/testsahur</code> in your Discord server.</li>
|
|
</ol>
|
|
</div>
|
|
</div>
|
|
|
|
<footer class="text-center mt-5">
|
|
<p class="small text-muted">Flatlogic LAMP Engineer © <?= date('Y') ?></p>
|
|
</footer>
|
|
</div>
|
|
|
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
|
<script>
|
|
document.getElementById('resetBotBtn').addEventListener('click', function() {
|
|
const btn = this;
|
|
const btnText = document.getElementById('btnText');
|
|
const btnLoader = document.getElementById('btnLoader');
|
|
const statusDiv = document.getElementById('resetStatus');
|
|
|
|
if (!confirm('Are you sure you want to restart the bot process?')) return;
|
|
|
|
btn.disabled = true;
|
|
btnText.classList.add('d-none');
|
|
btnLoader.classList.remove('d-none');
|
|
statusDiv.classList.add('d-none');
|
|
|
|
fetch('api/reset_bot.php', {
|
|
method: 'POST'
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
btn.disabled = false;
|
|
btnText.classList.remove('d-none');
|
|
btnLoader.classList.add('d-none');
|
|
statusDiv.classList.remove('d-none');
|
|
|
|
if (data.success) {
|
|
statusDiv.className = 'hint mt-2 text-center text-success';
|
|
statusDiv.textContent = data.message + '. Refreshing page...';
|
|
setTimeout(() => location.reload(), 2000);
|
|
} else {
|
|
statusDiv.className = 'hint mt-2 text-center text-danger';
|
|
statusDiv.textContent = 'Error: ' + (data.error || 'Unknown error');
|
|
console.error(data.details);
|
|
}
|
|
})
|
|
.catch(error => {
|
|
btn.disabled = false;
|
|
btnText.classList.remove('d-none');
|
|
btnLoader.classList.add('d-none');
|
|
statusDiv.classList.remove('d-none');
|
|
statusDiv.className = 'hint mt-2 text-center text-danger';
|
|
statusDiv.textContent = 'Network error occurred.';
|
|
console.error(error);
|
|
});
|
|
});
|
|
</script>
|
|
</body>
|
|
</html>
|