433 lines
14 KiB
JavaScript
433 lines
14 KiB
JavaScript
|
|
// ============================================================================
|
||
|
|
// 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';
|
||
|
|
}
|
||
|
|
}
|