refactor: Modularize monolithic HTML control panel into organized components
This commit completes a major refactoring of the Miku control panel from a single 7,191-line monolithic HTML file to a modern modular architecture: CHANGES: - Extracted 872 lines of CSS into css/style.css - Created 10 specialized JavaScript modules (4,964 lines total): * core.js: Global state, utilities, initialization, polling system * servers.js: Server management and mood handling * modes.js: Evil mode, GPU selection, bipolar mode, scoreboard * actions.js: Autonomous/manual actions, custom prompts, reactions * image-gen.js: Image generation system * status.js: Status display and statistics * dm.js: DM user management and conversation analysis * chat.js: LLM chat interface with streaming and voice calls * memories.js: Cheshire Cat memory integration (episodic/declarative/procedural) * profile.js: Profile picture, album gallery, activities editor - Cleaned index.html to 1,351 lines (structure only, zero inline JS/CSS) - Removed 12 duplicate variable declarations - Maintained strict script load order for dependency resolution - Added backup comment to index.html.bak for historical reference VERIFICATION COMPLETED: ✓ All 191 functions/variables from original accounted for ✓ Cross-referenced with backup to ensure nothing lost ✓ All onclick handlers and modal systems validated ✓ No circular dependencies or broken references ✓ HTML structure integrity verified (11 tabs, all buttons/modals intact) ✓ CropperJS CDN links preserved The refactored code is production-ready with improved maintainability and clear separation of concerns.
This commit is contained in:
548
bot/static/js/dm.js
Normal file
548
bot/static/js/dm.js
Normal file
@@ -0,0 +1,548 @@
|
||||
// ============================================================================
|
||||
// Miku Control Panel — DM Management Module
|
||||
// ============================================================================
|
||||
|
||||
async function loadDMUsers() {
|
||||
try {
|
||||
const result = await apiCall('/dms/users');
|
||||
displayDMUsers(result.users);
|
||||
} catch (error) {
|
||||
console.error('Failed to load DM users:', error);
|
||||
}
|
||||
}
|
||||
|
||||
function displayDMUsers(users) {
|
||||
const container = document.getElementById('dm-users-list');
|
||||
|
||||
if (!users || users.length === 0) {
|
||||
container.innerHTML = '<p>No DM conversations found.</p>';
|
||||
return;
|
||||
}
|
||||
|
||||
let html = '<div class="dm-users-grid">';
|
||||
|
||||
users.forEach(user => {
|
||||
console.log(`👤 Processing user: ${user.username} (ID: ${user.user_id})`);
|
||||
|
||||
const lastMessage = user.last_message ?
|
||||
`Last: ${user.last_message.content}` :
|
||||
'No messages yet';
|
||||
|
||||
const lastTime = user.last_message ?
|
||||
new Date(user.last_message.timestamp).toLocaleString() :
|
||||
'Never';
|
||||
|
||||
html += `
|
||||
<div class="dm-user-card">
|
||||
<h4>👤 ${user.username}</h4>
|
||||
<p><strong>ID:</strong> ${user.user_id}</p>
|
||||
<p><strong>Total Messages:</strong> ${user.total_messages}</p>
|
||||
<p><strong>User Messages:</strong> ${user.user_messages}</p>
|
||||
<p><strong>Bot Messages:</strong> ${user.bot_messages}</p>
|
||||
<p><strong>Last Activity:</strong> ${lastTime}</p>
|
||||
<p><strong>Last Message:</strong> ${lastMessage}</p>
|
||||
<div class="dm-user-actions">
|
||||
<button class="view-chat-btn" data-user-id="${user.user_id}">💬 View Chat</button>
|
||||
<button class="analyze-user-btn" data-user-id="${user.user_id}" data-username="${user.username}" style="background: #9c27b0;">📊 Analyze</button>
|
||||
<button class="export-dms-btn" data-user-id="${user.user_id}">📤 Export</button>
|
||||
<button class="block-user-btn" data-user-id="${user.user_id}" data-username="${user.username}" style="background: #ff9800;">🚫 Block</button>
|
||||
<button class="delete-all-dms-btn" data-user-id="${user.user_id}" data-username="${user.username}" style="background: #f44336;">🗑️ Delete All</button>
|
||||
<button class="delete-user-completely-btn" data-user-id="${user.user_id}" data-username="${user.username}" style="background: #d32f2f;">💀 Delete User</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
});
|
||||
|
||||
html += '</div>';
|
||||
container.innerHTML = html;
|
||||
|
||||
// Add event listeners after HTML is inserted
|
||||
addDMUserEventListeners();
|
||||
}
|
||||
|
||||
function addDMUserEventListeners() {
|
||||
// Add event listeners for view chat buttons
|
||||
document.querySelectorAll('.view-chat-btn').forEach(button => {
|
||||
button.addEventListener('click', function() {
|
||||
const userId = this.getAttribute('data-user-id');
|
||||
console.log(`🎯 View chat clicked for user ID: ${userId} (type: ${typeof userId})`);
|
||||
viewUserConversations(userId);
|
||||
});
|
||||
});
|
||||
|
||||
// Add event listeners for export buttons
|
||||
document.querySelectorAll('.export-dms-btn').forEach(button => {
|
||||
button.addEventListener('click', function() {
|
||||
const userId = this.getAttribute('data-user-id');
|
||||
console.log(`🎯 Export clicked for user ID: ${userId} (type: ${typeof userId})`);
|
||||
exportUserDMs(userId);
|
||||
});
|
||||
});
|
||||
|
||||
// Add event listeners for analyze buttons
|
||||
document.querySelectorAll('.analyze-user-btn').forEach(button => {
|
||||
button.addEventListener('click', function() {
|
||||
const userId = this.getAttribute('data-user-id');
|
||||
const username = this.getAttribute('data-username');
|
||||
console.log(`🎯 Analyze clicked for user ID: ${userId} (type: ${typeof userId})`);
|
||||
analyzeUserInteraction(userId, username);
|
||||
});
|
||||
});
|
||||
|
||||
// Add event listeners for block buttons
|
||||
document.querySelectorAll('.block-user-btn').forEach(button => {
|
||||
button.addEventListener('click', function() {
|
||||
const userId = this.getAttribute('data-user-id');
|
||||
const username = this.getAttribute('data-username');
|
||||
console.log(`🎯 Block clicked for user ID: ${userId} (type: ${typeof userId})`);
|
||||
blockUser(userId, username);
|
||||
});
|
||||
});
|
||||
|
||||
// Add event listeners for delete all DMs buttons
|
||||
document.querySelectorAll('.delete-all-dms-btn').forEach(button => {
|
||||
button.addEventListener('click', function() {
|
||||
const userId = this.getAttribute('data-user-id');
|
||||
const username = this.getAttribute('data-username');
|
||||
console.log(`🎯 Delete all DMs clicked for user ID: ${userId} (type: ${typeof userId})`);
|
||||
deleteAllUserConversations(userId, username);
|
||||
});
|
||||
});
|
||||
|
||||
// Add event listeners for delete user completely buttons
|
||||
document.querySelectorAll('.delete-user-completely-btn').forEach(button => {
|
||||
button.addEventListener('click', function() {
|
||||
const userId = this.getAttribute('data-user-id');
|
||||
const username = this.getAttribute('data-username');
|
||||
console.log(`🎯 Delete user completely clicked for user ID: ${userId} (type: ${typeof userId})`);
|
||||
deleteUserCompletely(userId, username);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function viewUserConversations(userId) {
|
||||
try {
|
||||
// Ensure userId is always treated as a string
|
||||
const userIdStr = String(userId);
|
||||
console.log(`🔍 Loading conversations for user ${userIdStr} (type: ${typeof userIdStr})`);
|
||||
console.log(`🔍 Original userId: ${userId} (type: ${typeof userId})`);
|
||||
console.log(`🔍 userIdStr: ${userIdStr} (type: ${typeof userIdStr})`);
|
||||
|
||||
const result = await apiCall(`/dms/users/${userIdStr}/conversations?limit=100`);
|
||||
|
||||
console.log('📡 API Response:', result);
|
||||
console.log('📡 API URL called:', `/dms/users/${userIdStr}/conversations?limit=100`);
|
||||
|
||||
if (result.conversations && result.conversations.length > 0) {
|
||||
console.log(`✅ Found ${result.conversations.length} conversations`);
|
||||
displayUserConversations(userIdStr, result.conversations);
|
||||
} else {
|
||||
console.log('⚠️ No conversations found in response');
|
||||
showNotification('No conversations found for this user', 'info');
|
||||
// Go back to user list
|
||||
loadDMUsers();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to load user conversations:', error);
|
||||
}
|
||||
}
|
||||
|
||||
function displayUserConversations(userId, conversations) {
|
||||
console.log(`🎨 Displaying conversations for user ${userId}:`, conversations);
|
||||
|
||||
// Create a modal or expand the user card to show conversations
|
||||
const container = document.getElementById('dm-users-list');
|
||||
|
||||
let html = `
|
||||
<div class="conversation-view">
|
||||
<button onclick="loadDMUsers()" style="margin-bottom: 1rem;">← Back to DM Users</button>
|
||||
<h4>💬 Conversations with User ${userId}</h4>
|
||||
<div class="conversations-list">
|
||||
`;
|
||||
|
||||
if (!conversations || conversations.length === 0) {
|
||||
html += '<p>No conversations found for this user.</p>';
|
||||
} else {
|
||||
conversations.forEach((msg, index) => {
|
||||
console.log(`📝 Processing message ${index}:`, msg);
|
||||
const timestamp = new Date(msg.timestamp).toLocaleString();
|
||||
const sender = msg.is_bot_message ? '🤖 Miku' : '👤 User';
|
||||
const content = msg.content || '[No text content]';
|
||||
|
||||
const messageId = msg.message_id || msg.timestamp; // Use message_id or timestamp as identifier
|
||||
const escapedContent = content.replace(/'/g, "\\'").replace(/"/g, '\\"');
|
||||
|
||||
// Debug: Log message details
|
||||
console.log(`📝 Message ${index}: id=${messageId}, is_bot=${msg.is_bot_message}, content="${content.substring(0, 30)}..."`);
|
||||
|
||||
// Only show delete button for bot messages (Miku can only delete her own messages)
|
||||
const deleteButton = msg.is_bot_message ?
|
||||
`<button class="delete-message-btn" onclick="deleteConversation('${userId}', '${messageId}', '${escapedContent}')"
|
||||
style="background: #f44336; color: white; border: none; padding: 2px 6px; font-size: 12px; border-radius: 3px; margin-left: 10px;"
|
||||
title="Delete this Miku message (ID: ${messageId})">
|
||||
🗑️ Delete
|
||||
</button>` : '';
|
||||
|
||||
html += `
|
||||
<div class="conversation-message ${msg.is_bot_message ? 'bot-message' : 'user-message'}">
|
||||
<div class="message-header">
|
||||
<span class="sender">${sender}</span>
|
||||
<span class="timestamp">${timestamp}</span>
|
||||
${deleteButton}
|
||||
</div>
|
||||
<div class="message-content">${content}</div>
|
||||
${msg.attachments && msg.attachments.length > 0 ? `
|
||||
<div class="message-attachments">
|
||||
<strong>📎 Attachments:</strong>
|
||||
${msg.attachments.map(att => `
|
||||
<div class="attachment">
|
||||
- ${att.filename} (${att.size} bytes)
|
||||
<a href="${att.url}" target="_blank">🔗 View</a>
|
||||
</div>
|
||||
`).join('')}
|
||||
</div>
|
||||
` : ''}
|
||||
${msg.reactions && msg.reactions.length > 0 ? `
|
||||
<div class="message-reactions">
|
||||
${msg.reactions.map(reaction => {
|
||||
const reactionTime = new Date(reaction.added_at).toLocaleString();
|
||||
const reactorType = reaction.is_bot ? 'bot-reaction' : 'user-reaction';
|
||||
const reactorLabel = reaction.is_bot ? '🤖 Miku' : `👤 ${reaction.reactor_name}`;
|
||||
return `
|
||||
<div class="reaction-item" title="${reactorLabel} reacted at ${reactionTime}">
|
||||
<span class="reaction-emoji">${reaction.emoji}</span>
|
||||
<span class="reaction-by ${reactorType}">${reactorLabel}</span>
|
||||
</div>
|
||||
`;
|
||||
}).join('')}
|
||||
</div>
|
||||
` : ''}
|
||||
</div>
|
||||
`;
|
||||
});
|
||||
}
|
||||
|
||||
html += `
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
console.log('🎨 Generated HTML:', html);
|
||||
container.innerHTML = html;
|
||||
}
|
||||
|
||||
async function exportUserDMs(userId) {
|
||||
try {
|
||||
// Ensure userId is always treated as a string
|
||||
const userIdStr = String(userId);
|
||||
await apiCall(`/dms/users/${userIdStr}/export?format=txt`);
|
||||
showNotification(`DM export completed for user ${userIdStr}`);
|
||||
// You could trigger a download here if the file is accessible
|
||||
} catch (error) {
|
||||
console.error('Failed to export user DMs:', error);
|
||||
}
|
||||
}
|
||||
|
||||
async function deleteUserDMs(userId) {
|
||||
// Ensure userId is always treated as a string
|
||||
const userIdStr = String(userId);
|
||||
|
||||
if (!confirm(`Are you sure you want to delete all DM logs for user ${userIdStr}? This action cannot be undone.`)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await apiCall(`/dms/users/${userIdStr}`, 'DELETE');
|
||||
showNotification(`Deleted DM logs for user ${userIdStr}`);
|
||||
loadDMUsers(); // Refresh the list
|
||||
} catch (error) {
|
||||
console.error('Failed to delete user DMs:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// ========== User Blocking & Advanced Deletion Functions ==========
|
||||
|
||||
async function blockUser(userId, username) {
|
||||
const userIdStr = String(userId);
|
||||
|
||||
if (!confirm(`Are you sure you want to block ${username} (${userIdStr}) from sending DMs to Miku?`)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await apiCall(`/dms/users/${userIdStr}/block`, 'POST');
|
||||
showNotification(`${username} has been blocked from sending DMs`);
|
||||
loadDMUsers(); // Refresh the list
|
||||
} catch (error) {
|
||||
console.error('Failed to block user:', error);
|
||||
}
|
||||
}
|
||||
|
||||
async function unblockUser(userId, username) {
|
||||
const userIdStr = String(userId);
|
||||
|
||||
try {
|
||||
await apiCall(`/dms/users/${userIdStr}/unblock`, 'POST');
|
||||
showNotification(`${username} has been unblocked`);
|
||||
loadBlockedUsers(); // Refresh blocked users list
|
||||
} catch (error) {
|
||||
console.error('Failed to unblock user:', error);
|
||||
}
|
||||
}
|
||||
|
||||
async function deleteAllUserConversations(userId, username) {
|
||||
const userIdStr = String(userId);
|
||||
|
||||
if (!confirm(`⚠️ DELETE ALL CONVERSATIONS with ${username} (${userIdStr})?\n\nThis will:\n• Delete ALL Miku messages from Discord DM\n• Clear all conversation logs\n• Keep the user record\n\nThis action CANNOT be undone!\n\nClick OK to confirm deletion.`)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await apiCall(`/dms/users/${userIdStr}/conversations/delete-all`, 'POST');
|
||||
showNotification(`Bulk deletion queued for ${username} (deleting all Miku messages from Discord and logs)`);
|
||||
setTimeout(() => {
|
||||
loadDMUsers(); // Refresh after a delay to allow deletion to process
|
||||
}, 2000);
|
||||
} catch (error) {
|
||||
console.error('Failed to delete conversations:', error);
|
||||
}
|
||||
}
|
||||
|
||||
async function deleteUserCompletely(userId, username) {
|
||||
const userIdStr = String(userId);
|
||||
|
||||
if (!confirm(`🚨 COMPLETELY DELETE USER ${username} (${userIdStr})?\n\nThis will:\n• Delete ALL conversation history\n• Delete the entire user log file\n• Remove ALL traces of this user\n\nThis action is PERMANENT and CANNOT be undone!\n\nType "${username}" below to confirm:`)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const confirmName = prompt(`Type the username "${username}" to confirm complete deletion:`);
|
||||
if (confirmName !== username) {
|
||||
showNotification('Deletion cancelled - username did not match', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await apiCall(`/dms/users/${userIdStr}/delete-completely`, 'POST');
|
||||
showNotification(`${username} has been completely deleted from the system`);
|
||||
loadDMUsers(); // Refresh the list
|
||||
} catch (error) {
|
||||
console.error('Failed to delete user completely:', error);
|
||||
}
|
||||
}
|
||||
|
||||
async function deleteConversation(userId, conversationId, messageContent) {
|
||||
const userIdStr = String(userId);
|
||||
|
||||
if (!confirm(`Delete this Miku message from Discord and logs?\n\n"${messageContent.substring(0, 100)}${messageContent.length > 100 ? '...' : ''}"\n\nThis will:\n• Delete the message from Discord DM\n• Remove it from conversation logs\n\nNote: Only Miku's messages can be deleted.\nThis action cannot be undone.`)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await apiCall(`/dms/users/${userIdStr}/conversations/${conversationId}/delete`, 'POST');
|
||||
showNotification('Miku message deletion queued (deleting from both Discord and logs)');
|
||||
setTimeout(() => {
|
||||
viewUserConversations(userId); // Refresh after a short delay to allow deletion to process
|
||||
}, 1000);
|
||||
} catch (error) {
|
||||
console.error('Failed to delete conversation:', error);
|
||||
}
|
||||
}
|
||||
|
||||
async function analyzeUserInteraction(userId, username) {
|
||||
const userIdStr = String(userId);
|
||||
|
||||
if (!confirm(`Run DM interaction analysis for ${username}?\n\nThis will:\n• Analyze their messages from the last 24 hours\n• Generate a sentiment report\n• Send report to bot owner\n\nMinimum 3 messages required for analysis.`)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
showNotification(`Analyzing ${username}'s interactions...`, 'info');
|
||||
|
||||
const result = await apiCall(`/dms/users/${userIdStr}/analyze`, 'POST');
|
||||
|
||||
if (result.reported) {
|
||||
showNotification(`✅ Analysis complete! Report sent to bot owner for ${username}`);
|
||||
} else {
|
||||
showNotification(`📊 Analysis complete for ${username} (not enough messages or already reported today)`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to analyze user:', error);
|
||||
}
|
||||
}
|
||||
|
||||
async function runDailyAnalysis() {
|
||||
if (!confirm('Run the daily DM interaction analysis now?\n\nThis will:\n• Analyze all DM users from the last 24 hours\n• Report one significant interaction to the bot owner\n• Skip users already reported today\n\nNote: This runs automatically at 2 AM daily.')) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
showNotification('Starting DM interaction analysis...', 'info');
|
||||
|
||||
await apiCall('/dms/analysis/run', 'POST');
|
||||
showNotification('✅ DM analysis completed! Check bot owner\'s DMs for any reports.');
|
||||
} catch (error) {
|
||||
console.error('Failed to run DM analysis:', error);
|
||||
}
|
||||
}
|
||||
|
||||
async function viewAnalysisReports() {
|
||||
try {
|
||||
showNotification('Loading analysis reports...', 'info');
|
||||
|
||||
const result = await apiCall('/dms/analysis/reports?limit=50');
|
||||
displayAnalysisReports(result.reports);
|
||||
} catch (error) {
|
||||
console.error('Failed to load reports:', error);
|
||||
}
|
||||
}
|
||||
|
||||
function displayAnalysisReports(reports) {
|
||||
const container = document.getElementById('dm-users-list');
|
||||
|
||||
if (!reports || reports.length === 0) {
|
||||
container.innerHTML = `
|
||||
<div style="text-align: center; padding: 2rem;">
|
||||
<p>No analysis reports found yet.</p>
|
||||
<button onclick="loadDMUsers()" style="margin-top: 1rem;">← Back to DM Users</button>
|
||||
</div>
|
||||
`;
|
||||
return;
|
||||
}
|
||||
|
||||
let html = `
|
||||
<div style="margin-bottom: 1rem;">
|
||||
<button onclick="loadDMUsers()">← Back to DM Users</button>
|
||||
<span style="margin-left: 1rem; color: #aaa;">${reports.length} reports found</span>
|
||||
</div>
|
||||
<div style="display: grid; gap: 1rem;">
|
||||
`;
|
||||
|
||||
reports.forEach(report => {
|
||||
const sentimentColor =
|
||||
report.sentiment_score >= 5 ? '#4caf50' :
|
||||
report.sentiment_score <= -3 ? '#f44336' :
|
||||
'#2196f3';
|
||||
|
||||
const sentimentEmoji =
|
||||
report.sentiment_score >= 5 ? '😊' :
|
||||
report.sentiment_score <= -3 ? '😢' :
|
||||
'😐';
|
||||
|
||||
const timestamp = new Date(report.analyzed_at).toLocaleString();
|
||||
|
||||
html += `
|
||||
<div style="background: #2a2a2a; border-left: 4px solid ${sentimentColor}; padding: 1rem; border-radius: 4px;">
|
||||
<div style="display: flex; justify-content: space-between; align-items: start; margin-bottom: 0.5rem;">
|
||||
<div>
|
||||
<h4 style="margin: 0 0 0.25rem 0;">${sentimentEmoji} ${report.username}</h4>
|
||||
<p style="margin: 0; font-size: 0.85rem; color: #aaa;">User ID: ${report.user_id}</p>
|
||||
</div>
|
||||
<div style="text-align: right;">
|
||||
<div style="font-size: 1.2rem; font-weight: bold; color: ${sentimentColor};">
|
||||
${report.sentiment_score > 0 ? '+' : ''}${report.sentiment_score}/10
|
||||
</div>
|
||||
<div style="font-size: 0.75rem; color: #aaa; text-transform: uppercase;">
|
||||
${report.overall_sentiment}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="margin: 0.75rem 0; padding: 0.75rem; background: #1e1e1e; border-radius: 4px;">
|
||||
<strong>Miku's Feelings:</strong>
|
||||
<p style="margin: 0.5rem 0 0 0; font-style: italic;">"${report.your_feelings}"</p>
|
||||
</div>
|
||||
|
||||
${report.notable_moment ? `
|
||||
<div style="margin: 0.75rem 0; padding: 0.75rem; background: #1e1e1e; border-radius: 4px;">
|
||||
<strong>Notable Moment:</strong>
|
||||
<p style="margin: 0.5rem 0 0 0; font-style: italic;">"${report.notable_moment}"</p>
|
||||
</div>
|
||||
` : ''}
|
||||
|
||||
${report.key_behaviors && report.key_behaviors.length > 0 ? `
|
||||
<div style="margin: 0.75rem 0;">
|
||||
<strong>Key Behaviors:</strong>
|
||||
<ul style="margin: 0.5rem 0 0 0; padding-left: 1.5rem;">
|
||||
${report.key_behaviors.slice(0, 5).map(b => `<li>${b}</li>`).join('')}
|
||||
</ul>
|
||||
</div>
|
||||
` : ''}
|
||||
|
||||
<div style="margin-top: 0.75rem; padding-top: 0.75rem; border-top: 1px solid #444; font-size: 0.8rem; color: #aaa;">
|
||||
<span>📅 ${timestamp}</span>
|
||||
<span style="margin-left: 1rem;">💬 ${report.message_count} messages analyzed</span>
|
||||
<span style="margin-left: 1rem;">📄 ${report.filename}</span>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
});
|
||||
|
||||
html += '</div>';
|
||||
container.innerHTML = html;
|
||||
}
|
||||
|
||||
async function loadBlockedUsers() {
|
||||
try {
|
||||
const result = await apiCall('/dms/blocked-users');
|
||||
// Hide DM users list and show blocked users section
|
||||
document.getElementById('dm-users-list').style.display = 'none';
|
||||
document.getElementById('blocked-users-section').style.display = 'block';
|
||||
displayBlockedUsers(result.blocked_users);
|
||||
} catch (error) {
|
||||
console.error('Failed to load blocked users:', error);
|
||||
}
|
||||
}
|
||||
|
||||
function hideBlockedUsers() {
|
||||
// Show DM users list and hide blocked users section
|
||||
document.getElementById('dm-users-list').style.display = 'block';
|
||||
document.getElementById('blocked-users-section').style.display = 'none';
|
||||
loadDMUsers(); // Refresh DM users
|
||||
}
|
||||
|
||||
function displayBlockedUsers(blockedUsers) {
|
||||
const container = document.getElementById('blocked-users-list');
|
||||
|
||||
if (!blockedUsers || blockedUsers.length === 0) {
|
||||
container.innerHTML = '<p>No blocked users.</p>';
|
||||
return;
|
||||
}
|
||||
|
||||
let html = '<div class="blocked-users-grid">';
|
||||
|
||||
blockedUsers.forEach(user => {
|
||||
html += `
|
||||
<div class="blocked-user-card">
|
||||
<h4>🚫 ${user.username}</h4>
|
||||
<p><strong>ID:</strong> ${user.user_id}</p>
|
||||
<p><strong>Blocked:</strong> ${new Date(user.blocked_at).toLocaleString()}</p>
|
||||
<p><strong>Blocked by:</strong> ${user.blocked_by}</p>
|
||||
<div class="blocked-user-actions">
|
||||
<button onclick="unblockUser('${user.user_id}', '${user.username}')" style="background: #4caf50;">✅ Unblock</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
});
|
||||
|
||||
html += '</div>';
|
||||
container.innerHTML = html;
|
||||
}
|
||||
|
||||
async function exportAllDMs() {
|
||||
try {
|
||||
const result = await apiCall('/dms/users');
|
||||
|
||||
let exportCount = 0;
|
||||
for (const user of (result.users || [])) {
|
||||
try {
|
||||
await exportUserDMs(user.user_id);
|
||||
exportCount++;
|
||||
} catch (e) {
|
||||
console.error(`Failed to export DMs for user ${user.user_id}:`, e);
|
||||
}
|
||||
}
|
||||
showNotification(`Exported DMs for ${exportCount} users`);
|
||||
} catch (error) {
|
||||
console.error('Failed to export all DMs:', error);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user