document.addEventListener('DOMContentLoaded', () => { // DOM Elements const loginButton = document.getElementById('loginButton'); const logoutButton = document.getElementById('logoutButton'); const userInfo = document.getElementById('userInfo'); const userPubkey = document.getElementById('userPubkey'); const authSection = document.getElementById('auth-section'); const mainContent = document.getElementById('main-content'); const botsList = document.getElementById('bots-list'); const createBotBtn = document.getElementById('create-bot-btn'); const saveBotBtn = document.getElementById('save-bot-btn'); const generateKeypair = document.getElementById('generateKeypair'); const keypairInput = document.getElementById('keypair-input'); // Bootstrap Modal let createBotModal; if (typeof bootstrap !== 'undefined') { createBotModal = new bootstrap.Modal(document.getElementById('createBotModal')); } // State let currentUser = null; const API_ENDPOINT = ''; // Check if already logged in checkAuth(); // Event Listeners loginButton.addEventListener('click', login); logoutButton.addEventListener('click', logout); createBotBtn.addEventListener('click', showCreateBotModal); saveBotBtn.addEventListener('click', createBot); generateKeypair.addEventListener('change', toggleKeypairInput); // Functions async function checkAuth() { const token = localStorage.getItem('authToken'); if (!token) { showAuthSection(); return; } try { const response = await fetch(`${API_ENDPOINT}/api/auth/verify`, { headers: { 'Authorization': token } }); if (response.ok) { const data = await response.json(); currentUser = data.pubkey; showMainContent(); fetchBots(); } else { // Token invalid localStorage.removeItem('authToken'); showAuthSection(); } } catch (error) { console.error('Auth check failed:', error); showAuthSection(); } } async function login() { if (!window.nostr) { alert('Nostr extension not found. Please install a NIP-07 compatible extension like nos2x or Alby.'); return; } try { // Get user's public key const pubkey = await window.nostr.getPublicKey(); // Create challenge event for signing const event = { kind: 22242, created_at: Math.floor(Date.now() / 1000), tags: [['challenge', 'nostr-poster-auth']], content: 'Authenticate with Nostr Poster' }; // Sign the event const signedEvent = await window.nostr.signEvent(event); // Send to server for verification const response = await fetch(`${API_ENDPOINT}/api/auth/login`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ pubkey: pubkey, signature: signedEvent.sig, event: JSON.stringify(signedEvent) }) }); if (response.ok) { const data = await response.json(); localStorage.setItem('authToken', data.token); currentUser = pubkey; showMainContent(); fetchBots(); } else { alert('Authentication failed'); } } catch (error) { console.error('Login failed:', error); alert('Login failed: ' + error.message); } } function logout() { localStorage.removeItem('authToken'); currentUser = null; showAuthSection(); } function showAuthSection() { authSection.classList.remove('d-none'); mainContent.classList.add('d-none'); loginButton.classList.remove('d-none'); userInfo.classList.add('d-none'); logoutButton.classList.add('d-none'); } function showMainContent() { authSection.classList.add('d-none'); mainContent.classList.remove('d-none'); loginButton.classList.add('d-none'); userInfo.classList.remove('d-none'); logoutButton.classList.remove('d-none'); // Truncate pubkey for display const shortPubkey = currentUser.substring(0, 8) + '...' + currentUser.substring(currentUser.length - 4); userPubkey.textContent = shortPubkey; } async function fetchBots() { try { const token = localStorage.getItem('authToken'); const response = await fetch(`${API_ENDPOINT}/api/bots`, { headers: { 'Authorization': token } }); if (response.ok) { const bots = await response.json(); renderBots(bots); } else { console.error('Failed to fetch bots'); } } catch (error) { console.error('Error fetching bots:', error); } } function renderBots(bots) { botsList.innerHTML = ''; if (bots.length === 0) { botsList.innerHTML = `

You don't have any bots yet.

`; return; } bots.forEach(bot => { // Create a status badge let statusBadge = ''; if (bot.post_config?.enabled) { statusBadge = 'Active'; } else { statusBadge = 'Inactive'; } // Generate a profile image based on the bot's pubkey (just a colored square) const profileColor = generateColorFromString(bot.pubkey); const initials = (bot.name || 'Bot').substring(0, 2).toUpperCase(); const card = document.createElement('div'); card.className = 'col-md-4 mb-4'; card.innerHTML = `
${initials}
${bot.name}
${bot.display_name || ''}
${statusBadge}

${bot.bio || 'No bio'}

npub...${bot.pubkey.substring(bot.pubkey.length - 8)}

`; botsList.appendChild(card); }); // Helper function to generate a color from a string function generateColorFromString(str) { let hash = 0; for (let i = 0; i < str.length; i++) { hash = str.charCodeAt(i) + ((hash << 5) - hash); } // Use the hash to create a hue in the purple range (260-290) const hue = ((hash % 30) + 260) % 360; return `hsl(${hue}, 70%, 60%)`; } } function showCreateBotModal() { // Reset form document.getElementById('create-bot-form').reset(); generateKeypair.checked = true; keypairInput.classList.add('d-none'); // Show modal createBotModal.show(); } function toggleKeypairInput() { if (generateKeypair.checked) { keypairInput.classList.add('d-none'); } else { keypairInput.classList.remove('d-none'); } } async function createBot() { const name = document.getElementById('botName').value; const displayName = document.getElementById('botDisplayName').value; const bio = document.getElementById('botBio').value; const nip05 = document.getElementById('botNip05').value; if (!name) { alert('Bot name is required.'); return; } let pubkey = ''; let privkey = ''; if (!generateKeypair.checked) { pubkey = document.getElementById('botPubkey').value; privkey = document.getElementById('botPrivkey').value; if (!pubkey || !privkey) { alert('Both public and private keys are required when not generating a keypair.'); return; } } try { const token = localStorage.getItem('authToken'); const response = await fetch(`${API_ENDPOINT}/api/bots`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': token }, body: JSON.stringify({ name, display_name: displayName, bio, nip05, pubkey, encrypted_privkey: privkey }) }); if (response.ok) { createBotModal.hide(); fetchBots(); } else { const error = await response.json(); alert(`Failed to create bot: ${error.error}`); } } catch (error) { console.error('Error creating bot:', error); alert('Error creating bot: ' + error.message); } } const togglePrivkeyBtn = document.getElementById('togglePrivkey'); if (togglePrivkeyBtn) { togglePrivkeyBtn.addEventListener('click', function() { const privkeyInput = document.getElementById('botPrivkey'); const eyeIcon = this.querySelector('svg'); if (privkeyInput.type === 'password') { privkeyInput.type = 'text'; eyeIcon.innerHTML = ` `; } else { privkeyInput.type = 'password'; eyeIcon.innerHTML = ` `; } }); } });