Slipstream 172f5907b3
feat: Implement custom bot management dashboard
- Add `custom_bot_manager.py` for core bot lifecycle management.
- Introduce new API endpoints for custom bot status, start, stop, restart, and log retrieval.
- Extend `UserSettings` and `GlobalSettings` models with custom bot configuration options (token, enabled, prefix, status).
- Create a dedicated "Custom Bot" page in the dashboard (`custom-bot.html`) with associated JavaScript to configure settings and control the bot.
- Integrate custom bot initialization into the application startup.
2025-05-21 18:11:17 -06:00

276 lines
8.0 KiB
JavaScript

/**
* Custom Bot Management
* This module handles the custom bot functionality in the dashboard.
*/
// Status constants
const BOT_STATUS = {
NOT_CREATED: 'not_created',
RUNNING: 'running',
STOPPED: 'stopped',
ERROR: 'error'
};
// DOM elements
let botTokenInput;
let botPrefixInput;
let botStatusTypeSelect;
let botStatusTextInput;
let saveBotConfigButton;
let startBotButton;
let stopBotButton;
let statusDot;
let statusText;
let botError;
/**
* Initialize the custom bot functionality
*/
function initCustomBot() {
// Get DOM elements
botTokenInput = document.getElementById('bot-token-input');
botPrefixInput = document.getElementById('bot-prefix-input');
botStatusTypeSelect = document.getElementById('bot-status-type-select');
botStatusTextInput = document.getElementById('bot-status-text-input');
saveBotConfigButton = document.getElementById('save-bot-config-button');
startBotButton = document.getElementById('start-bot-button');
stopBotButton = document.getElementById('stop-bot-button');
statusDot = document.getElementById('status-dot');
statusText = document.getElementById('status-text');
botError = document.getElementById('bot-error');
// Add event listeners
if (saveBotConfigButton) {
saveBotConfigButton.addEventListener('click', saveCustomBotConfig);
}
if (startBotButton) {
startBotButton.addEventListener('click', startCustomBot);
}
if (stopBotButton) {
stopBotButton.addEventListener('click', stopCustomBot);
}
// Load custom bot settings
loadCustomBotSettings();
// Check bot status periodically
checkBotStatus();
setInterval(checkBotStatus, 10000); // Check every 10 seconds
}
/**
* Load custom bot settings from the server
*/
async function loadCustomBotSettings() {
try {
const response = await API.get('/dashboard/api/settings');
// Fill in the form fields
if (botTokenInput && response.custom_bot_token) {
botTokenInput.value = response.custom_bot_token;
}
if (botPrefixInput) {
botPrefixInput.value = response.custom_bot_prefix || '!';
}
if (botStatusTypeSelect) {
botStatusTypeSelect.value = response.custom_bot_status_type || 'listening';
}
if (botStatusTextInput) {
botStatusTextInput.value = response.custom_bot_status_text || '!help';
}
} catch (error) {
console.error('Error loading custom bot settings:', error);
Toast.error('Failed to load custom bot settings');
}
}
/**
* Save custom bot configuration
*/
async function saveCustomBotConfig() {
try {
// Validate inputs
if (!botTokenInput.value) {
Toast.error('Bot token is required');
return;
}
if (!botPrefixInput.value) {
Toast.error('Command prefix is required');
return;
}
if (!botStatusTextInput.value) {
Toast.error('Status text is required');
return;
}
// Get current settings first
const currentSettings = await API.get('/dashboard/api/settings');
// Prepare the settings object with updated bot settings
const settings = {
...currentSettings,
custom_bot_token: botTokenInput.value,
custom_bot_prefix: botPrefixInput.value,
custom_bot_status_type: botStatusTypeSelect.value,
custom_bot_status_text: botStatusTextInput.value
};
// Save the settings
await API.put('/dashboard/api/settings', settings);
Toast.success('Custom bot configuration saved successfully');
// Check bot status after saving
checkBotStatus();
} catch (error) {
console.error('Error saving custom bot configuration:', error);
Toast.error('Failed to save custom bot configuration');
}
}
/**
* Start the custom bot
*/
async function startCustomBot() {
try {
startBotButton.disabled = true;
startBotButton.textContent = 'Starting...';
await API.post('/dashboard/api/custom-bot/start');
Toast.success('Custom bot started successfully');
// Check bot status after starting
checkBotStatus();
} catch (error) {
console.error('Error starting custom bot:', error);
Toast.error('Failed to start custom bot: ' + (error.response?.data?.detail || error.message));
// Re-enable the button
startBotButton.disabled = false;
startBotButton.textContent = 'Start Bot';
}
}
/**
* Stop the custom bot
*/
async function stopCustomBot() {
try {
stopBotButton.disabled = true;
stopBotButton.textContent = 'Stopping...';
await API.post('/dashboard/api/custom-bot/stop');
Toast.success('Custom bot stopped successfully');
// Check bot status after stopping
checkBotStatus();
} catch (error) {
console.error('Error stopping custom bot:', error);
Toast.error('Failed to stop custom bot: ' + (error.response?.data?.detail || error.message));
// Re-enable the button
stopBotButton.disabled = false;
stopBotButton.textContent = 'Stop Bot';
}
}
/**
* Check the status of the custom bot
*/
async function checkBotStatus() {
try {
const response = await API.get('/dashboard/api/custom-bot/status');
// Update the status indicator
updateStatusIndicator(response);
// Update button states
updateButtonStates(response);
// Show error if any
if (response.error && botError) {
botError.textContent = response.error;
botError.classList.remove('hidden');
} else if (botError) {
botError.classList.add('hidden');
}
} catch (error) {
console.error('Error checking custom bot status:', error);
// Set status to unknown
if (statusDot) {
statusDot.className = 'w-4 h-4 rounded-full bg-gray-400 mr-2';
}
if (statusText) {
statusText.textContent = 'Unable to check status';
}
// Disable buttons
if (startBotButton) {
startBotButton.disabled = true;
}
if (stopBotButton) {
stopBotButton.disabled = true;
}
}
}
/**
* Update the status indicator based on the bot status
*/
function updateStatusIndicator(status) {
if (!statusDot || !statusText) return;
if (status.is_running) {
statusDot.className = 'w-4 h-4 rounded-full bg-green-500 mr-2';
statusText.textContent = 'Bot is running';
} else if (status.status === BOT_STATUS.ERROR) {
statusDot.className = 'w-4 h-4 rounded-full bg-red-500 mr-2';
statusText.textContent = 'Bot has an error';
} else if (status.exists) {
statusDot.className = 'w-4 h-4 rounded-full bg-yellow-500 mr-2';
statusText.textContent = 'Bot is stopped';
} else {
statusDot.className = 'w-4 h-4 rounded-full bg-gray-400 mr-2';
statusText.textContent = 'Bot not created yet';
}
}
/**
* Update button states based on the bot status
*/
function updateButtonStates(status) {
if (!startBotButton || !stopBotButton) return;
if (status.is_running) {
startBotButton.disabled = true;
stopBotButton.disabled = false;
startBotButton.textContent = 'Bot Running';
stopBotButton.textContent = 'Stop Bot';
} else if (status.exists) {
startBotButton.disabled = false;
stopBotButton.disabled = true;
startBotButton.textContent = 'Start Bot';
stopBotButton.textContent = 'Bot Stopped';
} else {
startBotButton.disabled = false;
stopBotButton.disabled = true;
startBotButton.textContent = 'Create & Start Bot';
stopBotButton.textContent = 'Stop Bot';
}
}
// Export the initialization function
window.initCustomBot = initCustomBot;