- Consolidated all .bak.* files from bot/ directory into backups/2025-12-07/ - Moved unused autonomous_wip.py to backups (verified not imported anywhere) - Relocated old .bot.bak.80825/ backup directory into backups/2025-12-07/old-bot-bak-80825/ - Preserved autonomous_v1_legacy.py as it is still actively used by autonomous.py - Created new backups/ directory with date-stamped subdirectory for better organization
359 lines
10 KiB
HTML
359 lines
10 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<title>Miku Control Panel</title>
|
|
<style>
|
|
body {
|
|
margin: 0;
|
|
display: flex;
|
|
font-family: monospace;
|
|
background-color: #121212;
|
|
color: #fff;
|
|
}
|
|
|
|
.panel {
|
|
width: 60%;
|
|
padding: 2rem;
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
.logs {
|
|
width: 40%;
|
|
height: 100vh;
|
|
background-color: #000;
|
|
color: #0f0;
|
|
padding: 1rem;
|
|
overflow-y: scroll;
|
|
font-size: 0.85rem;
|
|
border-left: 2px solid #333;
|
|
}
|
|
|
|
select, button, input {
|
|
margin: 0.4rem 0.5rem 0.4rem 0;
|
|
padding: 0.4rem;
|
|
background: #333;
|
|
color: #fff;
|
|
border: 1px solid #555;
|
|
}
|
|
|
|
.section {
|
|
margin-bottom: 2rem;
|
|
}
|
|
|
|
pre {
|
|
white-space: pre-wrap;
|
|
background: #1e1e1e;
|
|
padding: 1rem;
|
|
border: 1px solid #333;
|
|
}
|
|
|
|
h1, h3 {
|
|
color: #61dafb;
|
|
}
|
|
|
|
#notification {
|
|
position: fixed;
|
|
bottom: 20px;
|
|
right: 20px;
|
|
background-color: #222;
|
|
color: #fff;
|
|
padding: 1rem;
|
|
border: 1px solid #555;
|
|
border-radius: 8px;
|
|
opacity: 0.95;
|
|
display: none;
|
|
z-index: 1000;
|
|
font-size: 0.9rem;
|
|
}
|
|
|
|
</style>
|
|
</head>
|
|
<body>
|
|
|
|
<div class="panel">
|
|
<h1>Miku Control Panel</h1>
|
|
|
|
<div class="section">
|
|
<label for="mood">Mood:</label>
|
|
<select id="mood">
|
|
<option value="angry">💢 angry</option>
|
|
<option value="asleep">💤 asleep</option>
|
|
<option value="bubbly">🫧 bubbly</option>
|
|
<option value="curious">👀 curious</option>
|
|
<option value="excited">✨ excited</option>
|
|
<option value="flirty">🫦 flirty</option>
|
|
<option value="irritated">😒 irritated</option>
|
|
<option value="melancholy">🍷 melancholy</option>
|
|
<option value="neutral" selected>neutral</option>
|
|
<option value="romantic">💌 romantic</option>
|
|
<option value="serious">👔 serious</option>
|
|
<option value="shy">👉👈 shy</option>
|
|
<option value="silly">🪿 silly</option>
|
|
<option value="sleepy">🌙 sleepy</option>
|
|
</select>
|
|
<button onclick="setMood()">Set Mood</button>
|
|
<button onclick="resetMood()">Reset Mood</button>
|
|
<button onclick="calmMiku()">Calm</button>
|
|
</div>
|
|
|
|
<div class="section">
|
|
<button onclick="sleep()">Sleep</button>
|
|
<button onclick="wake()">Wake</button>
|
|
<button onclick="bedtime()">Bedtime</button>
|
|
<button onclick="triggerAutonomous('general')">Say Something General</button>
|
|
<button onclick="triggerAutonomous('engage_user')">Engage Random User</button>
|
|
<button onclick="triggerAutonomous('tweet')">Send Tweet</button>
|
|
</div>
|
|
|
|
<div class="section">
|
|
<input id="user_id" placeholder="User ID" oninput="syncUserId()" />
|
|
<button onclick="resetConvo()">Reset Conversation</button>
|
|
<button onclick="loadHistory()">Load History</button>
|
|
</div>
|
|
|
|
<div class="status section">
|
|
<h3>Status</h3>
|
|
<pre id="status_text">Loading...</pre>
|
|
</div>
|
|
|
|
<div class="conversation section">
|
|
<h3>Conversation History</h3>
|
|
<pre id="conversation_text">No history loaded.</pre>
|
|
</div>
|
|
|
|
<div class="custom prompt">
|
|
<h3>🎙️ Send Custom Prompt to Miku</h3>
|
|
<textarea id="customPrompt" placeholder="e.g. Talk about how nice the weather is today" rows="3" style="width: 100%;"></textarea>
|
|
<br>
|
|
<button onclick="sendCustomPrompt()">Send Prompt</button>
|
|
<p id="customStatus" style="color: green;"></p>
|
|
</div>
|
|
|
|
<div class="manual section">
|
|
<h3>🎭 Send Message as Miku (Manual Override)</h3>
|
|
<textarea id="manualMessage" placeholder="Type the message exactly as Miku should say it..." rows="3" style="width: 100%;"></textarea>
|
|
<br>
|
|
<input type="file" id="manualAttachment" multiple />
|
|
<br>
|
|
<input type="text" id="manualChannelId" placeholder="Channel ID..." style="width: 50%; margin-top: 0.5rem;" />
|
|
<br>
|
|
<button onclick="sendManualMessage()">Send as Miku</button>
|
|
<p id="manualStatus" style="color: green;"></p>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div class="logs" id="logs">
|
|
<strong>Live Logs</strong>
|
|
<pre id="log_output" style="background-color: #111; color: #0f0; padding: 10px; font-family: monospace; overflow-y: auto; height: 300px;">Connecting...</pre>
|
|
|
|
<strong style="margin-top: 2rem; display: block;">Last Full Prompt</strong>
|
|
<pre id="prompt_output" style="background-color: #111; color: #0f0; padding: 10px; font-family: monospace; overflow-y: auto; height: 300px;">Fetching prompt...</pre>
|
|
</div>
|
|
|
|
<script>
|
|
function showNotification(message, isError = false) {
|
|
const box = document.getElementById("notification");
|
|
box.textContent = message;
|
|
box.style.backgroundColor = isError ? "#8b0000" : "#222";
|
|
box.style.display = "block";
|
|
box.style.borderColor = isError ? "#ff4d4d" : "#555";
|
|
|
|
setTimeout(() => {
|
|
box.style.display = "none";
|
|
}, 4000);
|
|
}
|
|
|
|
async function post(url, data = {}) {
|
|
const res = await fetch(url, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: Object.keys(data).length ? JSON.stringify(data) : null
|
|
});
|
|
return await res.json();
|
|
}
|
|
|
|
function get(url) {
|
|
return fetch(url).then(res => res.json());
|
|
}
|
|
|
|
function getUserId() {
|
|
return document.getElementById('user_id').value.trim();
|
|
}
|
|
|
|
function syncUserId() {
|
|
localStorage.setItem("miku_user_id", getUserId());
|
|
}
|
|
|
|
function loadUserId() {
|
|
const saved = localStorage.getItem("miku_user_id");
|
|
if (saved) document.getElementById('user_id').value = saved;
|
|
}
|
|
|
|
async function setMood() {
|
|
const mood = document.getElementById('mood').value;
|
|
const res = await post('/mood', { mood });
|
|
showNotification(res.status === 'ok' ? `Mood set to ${res.new_mood}` : res.message);
|
|
refreshStatus();
|
|
}
|
|
|
|
async function resetMood() {
|
|
const res = await post('/mood/reset');
|
|
showNotification(`Mood reset to ${res.new_mood}`);
|
|
refreshStatus();
|
|
}
|
|
|
|
async function triggerAutonomous(type) {
|
|
if (!type) return showNotification("No action type specified.");
|
|
|
|
let endpoint = `/autonomous/${type}`;
|
|
const response = await fetch(endpoint, { method: 'POST' });
|
|
const data = await response.json();
|
|
showNotification(data.message);
|
|
}
|
|
|
|
async function calmMiku() {
|
|
const res = await post('/mood/calm');
|
|
showNotification(res.message);
|
|
refreshStatus();
|
|
}
|
|
|
|
async function sleep() {
|
|
const res = await post('/sleep');
|
|
showNotification(res.message);
|
|
refreshStatus();
|
|
}
|
|
|
|
async function wake() {
|
|
const res = await post('/wake');
|
|
showNotification(res.message);
|
|
refreshStatus();
|
|
}
|
|
|
|
async function bedtime() {
|
|
const res = await post('/bedtime');
|
|
showNotification(res.message);
|
|
}
|
|
|
|
async function resetConvo() {
|
|
const userId = getUserId();
|
|
if (!userId) return showNotification("Please enter a user ID.");
|
|
const res = await post('/conversation/reset', { user_id: userId });
|
|
showNotification(res.message);
|
|
}
|
|
|
|
async function loadHistory() {
|
|
const userId = getUserId();
|
|
if (!userId) return showNotification("Please enter a user ID.");
|
|
const history = await get(`/conversation/${userId}`);
|
|
if (!history.length) {
|
|
document.getElementById('conversation_text').textContent = "No conversation history.";
|
|
return;
|
|
}
|
|
const formatted = history.map(([user, miku]) => `User: ${user}\nMiku: ${miku}`).join('\n\n');
|
|
document.getElementById('conversation_text').textContent = formatted;
|
|
}
|
|
|
|
async function refreshStatus() {
|
|
const status = await get('/status');
|
|
document.getElementById('status_text').textContent = JSON.stringify(status, null, 2);
|
|
}
|
|
|
|
async function loadLogs() {
|
|
try {
|
|
const res = await fetch('/logs');
|
|
const text = await res.text();
|
|
document.getElementById('log_output').textContent = text;
|
|
} catch {
|
|
document.getElementById('log_output').textContent = "⚠️ Failed to fetch logs.";
|
|
}
|
|
}
|
|
|
|
async function loadPrompt() {
|
|
try {
|
|
const res = await fetch('/prompt');
|
|
const data = await res.json();
|
|
document.getElementById('prompt_output').textContent = data.prompt || "No prompt recorded.";
|
|
} catch {
|
|
document.getElementById('prompt_output').textContent = "⚠️ Failed to fetch prompt.";
|
|
}
|
|
}
|
|
|
|
async function sendCustomPrompt() {
|
|
const prompt = document.getElementById("customPrompt").value;
|
|
if (!prompt.trim()) {
|
|
showNotification("Please enter a prompt.");
|
|
return;
|
|
}
|
|
|
|
const res = await fetch("/autonomous/custom", {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify({ prompt })
|
|
});
|
|
|
|
const data = await res.json();
|
|
const statusEl = document.getElementById("customStatus");
|
|
if (data.success) {
|
|
statusEl.innerText = "✅ Sent prompt to Miku!";
|
|
document.getElementById("customPrompt").value = "";
|
|
} else {
|
|
statusEl.innerText = "❌ Failed to send message.";
|
|
statusEl.style.color = "red";
|
|
}
|
|
}
|
|
|
|
async function sendManualMessage() {
|
|
const message = document.getElementById("manualMessage").value.trim();
|
|
const files = document.getElementById("manualAttachment").files;
|
|
const channelId = document.getElementById("manualChannelId").value.trim();
|
|
|
|
if (!channelId) {
|
|
showNotification("Please enter a target channel ID.", true);
|
|
return;
|
|
}
|
|
|
|
if (!message && files.length === 0) {
|
|
showNotification("Please enter a message or attach at least one file.");
|
|
return;
|
|
}
|
|
|
|
const formData = new FormData();
|
|
formData.append("message", message);
|
|
formData.append("channel_id", channelId);
|
|
|
|
for (let i = 0; i < files.length; i++) {
|
|
formData.append("files", files[i]);
|
|
}
|
|
|
|
const res = await fetch("/manual/send", {
|
|
method: "POST",
|
|
body: formData
|
|
});
|
|
|
|
const data = await res.json();
|
|
const statusEl = document.getElementById("manualStatus");
|
|
|
|
if (data.success) {
|
|
statusEl.innerText = "✅ Message sent!";
|
|
document.getElementById("manualMessage").value = "";
|
|
document.getElementById("manualAttachment").value = "";
|
|
document.getElementById("manualChannelId").value = "";
|
|
} else {
|
|
statusEl.innerText = "❌ Failed to send.";
|
|
statusEl.style.color = "red";
|
|
}
|
|
}
|
|
|
|
loadUserId();
|
|
refreshStatus();
|
|
setInterval(refreshStatus, 5000);
|
|
setInterval(loadLogs, 3000);
|
|
setInterval(loadPrompt, 3000);
|
|
</script>
|
|
|
|
<div id="notification"></div>
|
|
</body>
|
|
</html>
|