// ============================================================================ // Miku Control Panel — Actions Module // Autonomous actions, manual actions, custom prompts, reactions // ============================================================================ // ===== Autonomous Actions ===== async function triggerAutonomous(actionType) { const selectedServer = document.getElementById('server-select').value; if (!actionType) { showNotification('No action type specified', 'error'); return; } try { let endpoint = `/autonomous/${actionType}`; if (selectedServer !== 'all') { endpoint += `?guild_id=${selectedServer}`; } const result = await apiCall(endpoint, 'POST'); showNotification(result.message || 'Action triggered successfully'); } catch (error) { console.error('Failed to trigger autonomous action:', error); } } function toggleEngageSubmenu() { const submenu = document.getElementById('engage-submenu'); submenu.style.display = submenu.style.display === 'none' ? 'block' : 'none'; } async function triggerEngageUser() { const selectedServer = document.getElementById('server-select').value; const userId = document.getElementById('engage-user-id').value.trim(); const engageType = document.querySelector('input[name="engage-type"]:checked').value; try { let endpoint = '/autonomous/engage'; const params = new URLSearchParams(); if (selectedServer !== 'all') { params.append('guild_id', selectedServer); } if (userId) { params.append('user_id', userId); } if (engageType !== 'random') { params.append('engagement_type', engageType); } params.append('manual_trigger', 'true'); if (params.toString()) { endpoint += `?${params.toString()}`; } const result = await apiCall(endpoint, 'POST'); showNotification(result.message || 'Engagement triggered successfully'); } catch (error) { console.error('Failed to trigger user engagement:', error); } } function toggleTweetSubmenu() { const submenu = document.getElementById('tweet-submenu'); submenu.style.display = submenu.style.display === 'none' ? 'block' : 'none'; } async function triggerShareTweet() { const selectedServer = document.getElementById('server-select').value; const tweetUrl = document.getElementById('tweet-url').value.trim(); if (tweetUrl) { const validDomains = ['x.com', 'twitter.com', 'fxtwitter.com']; let isValid = false; try { const urlObj = new URL(tweetUrl); const hostname = urlObj.hostname.toLowerCase(); isValid = validDomains.some(domain => hostname === domain || hostname.endsWith('.' + domain)); } catch (e) {} if (!isValid) { showNotification('Invalid tweet URL. Must be from x.com, twitter.com, or fxtwitter.com', 'error'); return; } } try { let endpoint = '/autonomous/tweet'; const params = new URLSearchParams(); if (selectedServer !== 'all') { params.append('guild_id', selectedServer); } if (tweetUrl) { params.append('tweet_url', tweetUrl); } if (params.toString()) { endpoint += `?${params.toString()}`; } const result = await apiCall(endpoint, 'POST'); showNotification(result.message || 'Tweet share triggered successfully'); } catch (error) { console.error('Failed to trigger tweet share:', error); } } // ===== Manual Actions ===== async function forceSleep() { try { await apiCall('/sleep', 'POST'); showNotification('Miku is now sleeping'); } catch (error) { console.error('Failed to force sleep:', error); } } async function wakeUp() { try { await apiCall('/wake', 'POST'); showNotification('Miku is now awake'); } catch (error) { console.error('Failed to wake up:', error); } } async function sendBedtime() { const selectedServer = document.getElementById('manual-server-select').value; console.log('🛏️ sendBedtime() called'); console.log('🛏️ Selected server value:', selectedServer); try { let endpoint = '/bedtime'; if (selectedServer !== 'all') { console.log('🛏️ Using guild_id (as string):', selectedServer); endpoint += `?guild_id=${selectedServer}`; } console.log('🛏️ Final endpoint:', endpoint); const result = await apiCall(endpoint, 'POST'); showNotification(result.message || 'Bedtime reminder sent successfully'); } catch (error) { console.error('Failed to send bedtime reminder:', error); } } async function resetConversation() { const userId = prompt('Enter user ID to reset conversation for:'); if (userId) { try { await apiCall('/conversation/reset', 'POST', { user_id: userId }); showNotification('Conversation reset'); } catch (error) { console.error('Failed to reset conversation:', error); } } } // ===== Manual Message ===== async function sendManualMessage() { const message = document.getElementById('manualMessage').value.trim(); const files = document.getElementById('manualAttachment').files; const targetType = document.getElementById('manual-target-type').value; const replyMessageId = document.getElementById('manualReplyMessageId').value.trim(); const replyMention = document.querySelector('input[name="manualReplyMention"]:checked').value === 'true'; const useWebhook = document.getElementById('manual-use-webhook').checked; const webhookPersona = document.querySelector('input[name="webhook-persona"]:checked')?.value || 'miku'; if (!message) { showNotification('Please enter a message', 'error'); return; } if (useWebhook && targetType === 'dm') { showNotification('Webhooks only work in channels, not DMs', 'error'); return; } let targetId, endpoint; if (targetType === 'dm') { targetId = document.getElementById('manualUserId').value.trim(); if (!targetId) { showNotification('Please enter a user ID for DM', 'error'); return; } endpoint = `/dm/${targetId}/manual`; } else { targetId = document.getElementById('manualChannelId').value.trim(); if (!targetId) { showNotification('Please enter a channel ID', 'error'); return; } endpoint = useWebhook ? '/manual/send-webhook' : '/manual/send'; } try { const formData = new FormData(); formData.append('message', message); if (useWebhook) { formData.append('persona', webhookPersona); } if (replyMessageId) { formData.append('reply_to_message_id', replyMessageId); formData.append('mention_author', replyMention); } if (targetType === 'dm') { if (files.length > 0) { for (let i = 0; i < files.length; i++) { formData.append('files', files[i]); } } } else { formData.append('channel_id', targetId); if (files.length > 0) { for (let i = 0; i < files.length; i++) { formData.append('files', files[i]); } } } const response = await fetch(endpoint, { method: 'POST', body: formData }); const result = await response.json(); if (response.ok) { showNotification('Message sent successfully'); document.getElementById('manualMessage').value = ''; document.getElementById('manualAttachment').value = ''; document.getElementById('manualReplyMessageId').value = ''; if (targetType === 'dm') { document.getElementById('manualUserId').value = ''; } else { document.getElementById('manualChannelId').value = ''; } document.getElementById('manualStatus').textContent = '✅ Message sent successfully!'; document.getElementById('manualStatus').style.color = 'green'; } else { throw new Error(result.message || 'Failed to send message'); } } catch (error) { console.error('Failed to send manual message:', error); showNotification(error.message || 'Failed to send message', 'error'); document.getElementById('manualStatus').textContent = '❌ Failed to send message'; document.getElementById('manualStatus').style.color = 'red'; } } // ===== Custom Prompt ===== function toggleCustomPromptTarget() { const targetType = document.getElementById('custom-prompt-target-type').value; const serverSection = document.getElementById('custom-prompt-server-section'); const dmSection = document.getElementById('custom-prompt-dm-section'); if (targetType === 'dm') { serverSection.style.display = 'none'; dmSection.style.display = 'inline'; } else { serverSection.style.display = 'inline'; dmSection.style.display = 'none'; } } function toggleWebhookOptions() { const useWebhook = document.getElementById('manual-use-webhook').checked; const webhookOptions = document.getElementById('webhook-persona-options'); const targetType = document.getElementById('manual-target-type'); if (useWebhook) { webhookOptions.style.display = 'block'; if (targetType.value === 'dm') { targetType.value = 'channel'; toggleManualMessageTarget(); } targetType.options[1].disabled = true; } else { webhookOptions.style.display = 'none'; targetType.options[1].disabled = false; } } function toggleManualMessageTarget() { const targetType = document.getElementById('manual-target-type').value; const channelSection = document.getElementById('manual-channel-section'); const dmSection = document.getElementById('manual-dm-section'); if (targetType === 'dm') { channelSection.style.display = 'none'; dmSection.style.display = 'block'; } else { channelSection.style.display = 'block'; dmSection.style.display = 'none'; } } async function sendCustomPrompt() { const prompt = document.getElementById('customPrompt').value.trim(); const targetType = document.getElementById('custom-prompt-target-type').value; const files = document.getElementById('customPromptAttachment').files; if (!prompt) { showNotification('Please enter a custom prompt', 'error'); return; } try { let endpoint; if (targetType === 'dm') { const userId = document.getElementById('custom-prompt-user-id').value.trim(); if (!userId) { showNotification('Please enter a user ID for DM', 'error'); return; } endpoint = `/dm/${userId}/custom`; } else { const selectedServer = document.getElementById('custom-prompt-server-select').value; endpoint = '/autonomous/custom'; if (selectedServer !== 'all') { endpoint += `?guild_id=${selectedServer}`; } } const result = await apiCall(endpoint, 'POST', { prompt: prompt }); showNotification(result.message || 'Custom prompt sent successfully'); document.getElementById('customPrompt').value = ''; document.getElementById('customPromptAttachment').value = ''; if (targetType === 'dm') { document.getElementById('custom-prompt-user-id').value = ''; } document.getElementById('customStatus').textContent = '✅ Custom prompt sent successfully!'; document.getElementById('customStatus').style.color = 'green'; } catch (error) { console.error('Failed to send custom prompt:', error); document.getElementById('customStatus').textContent = '❌ Failed to send custom prompt'; document.getElementById('customStatus').style.color = 'red'; } } function toggleCustomPrompt() { const customPromptSection = document.getElementById('custom-prompt-section'); if (customPromptSection) { customPromptSection.style.display = customPromptSection.style.display === 'none' ? 'block' : 'none'; } } // ===== Add Reaction ===== async function addReactionToMessage() { const messageId = document.getElementById('reactionMessageId').value.trim(); const channelId = document.getElementById('reactionChannelId').value.trim(); const emoji = document.getElementById('reactionEmoji').value.trim(); const statusElement = document.getElementById('reactionStatus'); if (!messageId) { showNotification('Please enter a message ID', 'error'); statusElement.textContent = '❌ Message ID is required'; statusElement.style.color = 'red'; return; } if (!channelId) { showNotification('Please enter a channel ID', 'error'); statusElement.textContent = '❌ Channel ID is required'; statusElement.style.color = 'red'; return; } if (!emoji) { showNotification('Please enter an emoji', 'error'); statusElement.textContent = '❌ Emoji is required'; statusElement.style.color = 'red'; return; } try { statusElement.textContent = '⏳ Adding reaction...'; statusElement.style.color = '#61dafb'; const formData = new FormData(); formData.append('message_id', messageId); formData.append('channel_id', channelId); formData.append('emoji', emoji); const response = await fetch('/messages/react', { method: 'POST', body: formData }); const result = await response.json(); if (response.ok && result.status === 'ok') { showNotification(`Reaction ${emoji} added successfully`); statusElement.textContent = `✅ Reaction ${emoji} added successfully!`; statusElement.style.color = 'green'; document.getElementById('reactionMessageId').value = ''; document.getElementById('reactionChannelId').value = ''; document.getElementById('reactionEmoji').value = ''; } else { throw new Error(result.message || 'Failed to add reaction'); } } catch (error) { console.error('Failed to add reaction:', error); showNotification(error.message || 'Failed to add reaction', 'error'); statusElement.textContent = `❌ ${error.message || 'Failed to add reaction'}`; statusElement.style.color = 'red'; } }