From cd92618351a7f87e62ae26011a152540d2d6266d Mon Sep 17 00:00:00 2001 From: Raven Scott Date: Thu, 20 Jun 2024 00:46:24 -0400 Subject: [PATCH 01/20] Add the beginnings of guilds --- app.js | 1013 +++++++++++++++++++++++--------------- chatBot/commands/ping.js | 2 +- commands.js | 27 +- index.html | 191 +++++-- style.css | 133 +++-- 5 files changed, 897 insertions(+), 469 deletions(-) diff --git a/app.js b/app.js index b1df6c3..72dcc80 100644 --- a/app.js +++ b/app.js @@ -5,7 +5,7 @@ import ServeDrive from 'serve-drive'; import Hyperdrive from 'hyperdrive'; import Corestore from 'corestore'; import { EventEmitter } from 'events'; -import fs from "fs"; +import fs from 'fs'; import handleCommand from './commands.js'; const agentAvatarPath = './assets/agent.png'; @@ -32,7 +32,7 @@ let servePort; let config = { userName: '', userAvatar: '', - rooms: [], + guilds: {}, registeredUsers: {} }; @@ -44,8 +44,8 @@ function getRandomPort() { return Math.floor(Math.random() * (65535 - 49152 + 1)) + 49152; } -function currentTopic() { - return document.querySelector('#chat-room-topic').innerText; +function currentGuildTopic() { + return document.querySelector('#chat-guild-topic').innerText; } function getCurrentPeerCount() { @@ -61,17 +61,57 @@ function updatePeerCount() { } } -async function joinRoom(topicStr) { - const topicBuffer = b4a.from(topicStr, 'hex'); - addRoomToList(topicStr); +async function processGuild(guildData) { + const parsedData = JSON.parse(guildData); + config.guilds[parsedData.guildTopic] = { + alias: parsedData.guildAlias, + rooms: parsedData.rooms, + owner: parsedData.owner + }; + writeConfigToFile("./config.json"); + renderGuildList(); + await joinGuild(parsedData.guildTopic); +} + +async function joinGuild(guildTopic) { + const guild = config.guilds[guildTopic]; + if (guild) { + for (const room of guild.rooms) { + await joinRoom(guildTopic, room.topic, room.alias); + } + } +} + +async function joinRoom(guildTopic, roomTopic, alias) { + const topicBuffer = b4a.from(roomTopic, 'hex'); + addRoomToList(guildTopic, roomTopic, alias); await joinSwarm(topicBuffer); } -async function createRoom(alias) { - const topicBuffer = crypto.randomBytes(32); - const topic = b4a.toString(topicBuffer, 'hex'); - addRoomToList(topic, alias); - await joinSwarm(topicBuffer); +async function createRoom(guildTopic, alias) { + const roomTopicBuffer = crypto.randomBytes(32); + const roomTopic = b4a.toString(roomTopicBuffer, 'hex'); + + config.guilds[guildTopic].rooms.push({ topic: roomTopic, alias: alias || truncateHash(roomTopic) }); + writeConfigToFile("./config.json"); + + addRoomToList(guildTopic, roomTopic, alias); + await joinSwarm(roomTopicBuffer); + + // Synchronize the new room with other peers + const roomMessage = JSON.stringify({ + type: 'room', + guildTopic, + room: { topic: roomTopic, alias: alias || truncateHash(roomTopic) } + }); + + const guildSwarm = activeRooms.find(room => room.topic === guildTopic); + if (guildSwarm) { + const peers = [...guildSwarm.swarm.connections]; + for (const peer of peers) { + peer.write(roomMessage); + } + } } async function listFiles() { @@ -81,6 +121,7 @@ async function listFiles() { } return files; } + async function deleteFile(filename) { await drive.del(`/files/${filename}`); } @@ -98,7 +139,7 @@ async function initialize() { const configExists = fs.existsSync("./config.json"); if (configExists) { loadConfigFromFile(); - renderRoomList(); + renderGuildList(); await connectToAllRooms(); } @@ -113,6 +154,31 @@ async function initialize() { document.addEventListener("DOMContentLoaded", (event) => { hljs.highlightAll(); }); + + document.addEventListener('createGuild', (event) => { + const { guildName } = event.detail; + createGuild(guildName); + }); + + document.addEventListener('addRoomToGuild', (event) => { + const { guildTopic, roomName } = event.detail; + createRoom(guildTopic, roomName); + }); + + document.addEventListener('manageGuild', (event) => { + const { guildTopic } = event.detail; + openManageGuildModal(guildTopic); + }); + + document.addEventListener('switchRoom', (event) => { + const { guildTopic, roomTopic } = event.detail; + if (!roomTopic) { + console.error('Invalid room topic:', roomTopic); + return; + } + console.log('Event switchRoom with roomTopic:', roomTopic); + switchRoom(guildTopic, roomTopic); + }); } catch (error) { console.error('Error during initialization:', error); } @@ -139,7 +205,10 @@ function setupEventListeners() { }); } if (createChatRoomButton) { - createChatRoomButton.addEventListener('click', createChatRoom); + createChatRoomButton.addEventListener('click', () => { + const guildTopic = currentGuildTopic(); + createRoom(guildTopic); + }); } if (joinChatRoomButton) { joinChatRoomButton.addEventListener('click', joinChatRoom); @@ -165,7 +234,21 @@ function setupEventListeners() { if (talkButton) { setupTalkButton(); } + + // Add event listeners only for room items + document.querySelectorAll('.room-item').forEach(item => { + item.addEventListener('click', () => { + const guildTopic = item.dataset.guildTopic; + const roomTopic = item.dataset.topic; + if (!roomTopic) { + console.error('Invalid room topic for item:', item); + return; + } + switchRoom(guildTopic, roomTopic); + }); + }); } + function handleIncomingMessage(messageObj) { console.log('Received message:', messageObj); // Debugging log @@ -209,10 +292,58 @@ function handleIncomingMessage(messageObj) { }).catch(error => { console.error(`Failed to store audio message:`, error); }); + } else if (messageObj.type === 'guild') { + const guildData = messageObj.guildData; + processGuild(guildData); + } else if (messageObj.type === 'room') { + const { guildTopic, room } = messageObj; + if (config.guilds[guildTopic]) { + config.guilds[guildTopic].rooms.push(room); + writeConfigToFile("./config.json"); + renderGuildList(); + joinRoom(guildTopic, room.topic, room.alias); + } + } else if (messageObj.type === 'remove-room') { + const { guildTopic, roomTopic } = messageObj; + if (config.guilds[guildTopic]) { + const roomIndex = config.guilds[guildTopic].rooms.findIndex(room => room.topic === roomTopic); + if (roomIndex !== -1) { + config.guilds[guildTopic].rooms.splice(roomIndex, 1); + writeConfigToFile("./config.json"); + renderGuildList(); + leaveRoom(roomTopic); + } + } + } else if (messageObj.type === 'rename-room') { + const { guildTopic, roomTopic, newAlias } = messageObj; + if (config.guilds[guildTopic]) { + const room = config.guilds[guildTopic].rooms.find(room => room.topic === roomTopic); + if (room) { + room.alias = newAlias; + writeConfigToFile("./config.json"); + + // Synchronize the room rename with other peers + const renameMessage = JSON.stringify({ + type: 'rename-room', + guildTopic, + roomTopic: topic, + newAlias + }); + + const guildSwarm = activeRooms.find(room => room.topic === guildTopic); + if (guildSwarm) { + const peers = [...guildSwarm.swarm.connections]; + for (const peer of peers) { + peer.write(renameMessage); + } + } + } + } } else { console.error('Received unknown message type:', messageObj); } } + async function handleConnection(connection, info) { console.log('New connection', info); @@ -231,6 +362,22 @@ async function handleConnection(connection, info) { console.error('Icon not found for user:', config.userName); } + // Sending the guilds and rooms information + for (const guildTopic in config.guilds) { + const guild = config.guilds[guildTopic]; + const guildMessage = JSON.stringify({ + type: 'guild', + guildData: JSON.stringify({ + guildTopic, + guildAlias: guild.alias, + rooms: guild.rooms, + owner: guild.owner + }) + }); + console.log('Sending guild information to new peer:', guildMessage); + connection.write(guildMessage); + } + connection.on('data', (data) => { const messageObj = JSON.parse(data.toString()); eventEmitter.emit('onMessage', messageObj); @@ -377,27 +524,37 @@ async function continueRegistration(regUsername) { writeConfigToFile("./config.json"); } -async function createChatRoom() { +async function createGuild(guildName) { const topicBuffer = crypto.randomBytes(32); const topic = b4a.toString(topicBuffer, 'hex'); - addRoomToList(topic); - await joinSwarm(topicBuffer); + + config.guilds[topic] = { + alias: guildName, + rooms: [], + owner: config.userName + }; + + addGuildToList(topic, guildName); + writeConfigToFile("./config.json"); } async function joinChatRoom(e) { e.preventDefault(); - const topicStr = document.querySelector('#join-chat-room-topic').value.trim(); + const guildTopic = document.querySelector('#join-guild-topic').value.trim(); + const roomTopic = document.querySelector('#join-chat-room-topic').value.trim(); // Validate the topic string - const isValidTopic = /^[0-9a-fA-F]{64}$/.test(topicStr); - if (!isValidTopic) { - alert('Invalid topic format. Please enter a 64-character hexadecimal string.'); + const isValidGuildTopic = /^[0-9a-fA-F]{64}$/.test(guildTopic); + const isValidRoomTopic = /^[0-9a-fA-F]{64}$/.test(roomTopic); + if (!isValidGuildTopic || !isValidRoomTopic) { + alert('Invalid topic format. Please enter 64-character hexadecimal strings.'); return; } - const topicBuffer = b4a.from(topicStr, 'hex'); - addRoomToList(topicStr); - await joinSwarm(topicBuffer); + const guildTopicBuffer = b4a.from(guildTopic, 'hex'); + const roomTopicBuffer = b4a.from(roomTopic, 'hex'); + addRoomToList(guildTopic, roomTopic); + await joinSwarm(roomTopicBuffer); } async function joinSwarm(topicBuffer) { @@ -424,18 +581,100 @@ async function joinSwarm(topicBuffer) { } } -function addRoomToList(topic, alias) { - const roomList = document.querySelector('#room-list'); - const roomItem = document.createElement('li'); - roomItem.textContent = alias || truncateHash(topic); - roomItem.dataset.topic = topic; +function addGuildToList(guildTopic, alias) { + const guildList = document.querySelector('#guild-list'); + const guildItem = document.createElement('li'); + guildItem.textContent = alias || truncateHash(guildTopic); + guildItem.dataset.guildTopic = guildTopic; + guildItem.classList.add('guild-item'); - roomItem.addEventListener('dblclick', () => enterEditMode(roomItem)); - roomItem.addEventListener('click', () => switchRoom(topic)); - roomList.appendChild(roomItem); + if (config.guilds[guildTopic].owner === config.userName) { + const manageButton = document.createElement('button'); + manageButton.textContent = 'Manage'; + manageButton.classList.add('mini-button', 'manage-guild-btn'); + manageButton.addEventListener('click', (e) => { + e.stopPropagation(); + openManageGuildModal(guildTopic); + }); + guildItem.appendChild(manageButton); + } - config.rooms.push({ topic, alias: alias || truncateHash(topic) }); - writeConfigToFile("./config.json"); + const roomList = document.createElement('ul'); + roomList.classList.add('room-list'); + guildItem.appendChild(roomList); + + guildList.appendChild(guildItem); + + // Add the rooms for this guild + config.guilds[guildTopic].rooms.forEach(room => { + addRoomToList(guildTopic, room.topic, room.alias); + }); +} + +function addRoomToList(guildTopic, roomTopic, alias) { + const guildItem = document.querySelector(`li[data-guild-topic="${guildTopic}"]`); + if (guildItem) { + let roomList = guildItem.querySelector('.room-list'); + if (!roomList) { + roomList = document.createElement('ul'); + roomList.classList.add('room-list'); + guildItem.appendChild(roomList); + } + + if (!roomList.querySelector(`li[data-topic="${roomTopic}"]`)) { + const roomItem = document.createElement('li'); + roomItem.textContent = alias || truncateHash(roomTopic); + roomItem.dataset.topic = roomTopic; + roomItem.dataset.guildTopic = guildTopic; + roomItem.classList.add('room-item'); + + roomItem.addEventListener('dblclick', () => enterEditMode(roomItem)); + roomItem.addEventListener('click', () => switchRoom(guildTopic, roomTopic)); + roomList.appendChild(roomItem); + } + } +} + +function openManageGuildModal(guildTopic) { + const guild = config.guilds[guildTopic]; + if (!guild) return; + + const manageGuildModal = document.getElementById('manage-guild-modal'); + manageGuildModal.dataset.guildTopic = guildTopic; + + const guildInfo = manageGuildModal.querySelector('#guild-info'); + guildInfo.innerHTML = `

${guild.alias}

`; + + const roomList = manageGuildModal.querySelector('#room-list'); + roomList.innerHTML = ''; + + guild.rooms.forEach(room => { + const roomItem = document.createElement('li'); + roomItem.textContent = room.alias; + roomItem.dataset.topic = room.topic; + + const editIcon = document.createElement('span'); + editIcon.textContent = '✏️'; + editIcon.classList.add('edit-icon'); + editIcon.addEventListener('click', (e) => { + e.stopPropagation(); + enterEditMode(roomItem); + }); + + const deleteIcon = document.createElement('span'); + deleteIcon.textContent = '❌'; + deleteIcon.classList.add('delete-icon'); + deleteIcon.addEventListener('click', (e) => { + e.stopPropagation(); + removeRoom(guildTopic, room.topic); + }); + + roomItem.appendChild(editIcon); + roomItem.appendChild(deleteIcon); + roomList.appendChild(roomItem); + }); + + manageGuildModal.classList.remove('hidden'); } function enterEditMode(roomItem) { @@ -472,10 +711,31 @@ function exitEditMode(roomItem, input, topic) { roomItem.textContent = newAlias; // Update the config with the new alias - const roomConfig = config.rooms.find(room => room.topic === topic); - if (roomConfig) { - roomConfig.alias = newAlias; - writeConfigToFile("./config.json"); + for (const guildTopic in config.guilds) { + const guild = config.guilds[guildTopic]; + const room = guild.rooms.find(room => room.topic === topic); + if (room) { + room.alias = newAlias; + writeConfigToFile("./config.json"); + + // Synchronize the room rename with other peers + const renameMessage = JSON.stringify({ + type: 'rename-room', + guildTopic, + roomTopic: topic, + newAlias + }); + + const guildSwarm = activeRooms.find(room => room.topic === guildTopic); + if (guildSwarm) { + const peers = [...guildSwarm.swarm.connections]; + for (const peer of peers) { + peer.write(renameMessage); + } + } + + break; + } } // Check if the edited room is the current room in view @@ -490,40 +750,83 @@ function exitEditMode(roomItem, input, topic) { } } -function switchRoom(topic) { - console.log('Switching to room:', topic); // Debugging log +function removeRoom(guildTopic, roomTopic) { + const guild = config.guilds[guildTopic]; + if (guild) { + const roomIndex = guild.rooms.findIndex(room => room.topic === roomTopic); + if (roomIndex !== -1) { + guild.rooms.splice(roomIndex, 1); + writeConfigToFile("./config.json"); + renderGuildList(); + leaveRoom(roomTopic); + + // Synchronize the room removal with other peers + const removeMessage = JSON.stringify({ + type: 'remove-room', + guildTopic, + roomTopic + }); + + const guildSwarm = activeRooms.find(room => room.topic === guildTopic); + if (guildSwarm) { + const peers = [...guildSwarm.swarm.connections]; + for (const peer of peers) { + peer.write(removeMessage); + } + } + } + } +} + +function switchRoom(guildTopic, roomTopic) { + if (!roomTopic) { + console.error('Invalid room topic:', roomTopic); + return; + } + + console.log('Switching to room:', roomTopic); const chatRoomTopic = document.querySelector('#chat-room-topic'); const chatRoomName = document.querySelector('#chat-room-name'); + const guild = config.guilds[guildTopic]; + + if (!guild) { + console.error('Guild not found:', guildTopic); + return; + } if (chatRoomTopic) { - chatRoomTopic.innerText = topic; // Set full topic here + chatRoomTopic.innerText = roomTopic; // Set full topic here } else { console.error('Element #chat-room-topic not found'); } if (chatRoomName) { - // Update the room name in the header - const room = config.rooms.find(r => r.topic === topic); - const roomName = room ? room.alias : truncateHash(topic); - chatRoomName.innerText = roomName; + // Find the room in the current guild + const room = guild.rooms.find(room => room.topic === roomTopic); + if (room) { + // Update the room name in the header + chatRoomName.innerText = room.alias; + } else { + console.error('Room not found in the current guild:', roomTopic); + } } else { console.error('Element #chat-room-name not found'); } - clearMessages(); - renderMessagesForRoom(topic); - updatePeerCount(); - - // Show chat UI elements + // Show the chat view document.querySelector('#chat').classList.remove('hidden'); document.querySelector('#setup').classList.add('hidden'); + + // Render the messages for the room + renderMessagesForRoom(roomTopic); } -function leaveRoom(topic) { +async function leaveRoom(topic) { const roomIndex = activeRooms.findIndex(room => room.topic === topic); if (roomIndex !== -1) { - const room = activeRooms[roomIndex]; - room.swarm.destroy(); + const { swarm, discovery } = activeRooms[roomIndex]; + await discovery.destroy(); + swarm.destroy(); activeRooms.splice(roomIndex, 1); } @@ -532,17 +835,237 @@ function leaveRoom(topic) { roomItem.remove(); } - config.rooms = config.rooms.filter(e => e.topic !== topic); - writeConfigToFile("./config.json"); + const messagesContainer = document.querySelector('#messages'); + if (messagesContainer) { + messagesContainer.innerHTML = ''; + } - if (activeRooms.length > 0) { - switchRoom(activeRooms[0].topic); - } else { - document.querySelector('#chat').classList.add('hidden'); - document.querySelector('#setup').classList.remove('hidden'); + const chatRoomName = document.querySelector('#chat-room-name'); + if (chatRoomName) { + chatRoomName.innerText = ''; + } + + const chatRoomTopic = document.querySelector('#chat-room-topic'); + if (chatRoomTopic) { + chatRoomTopic.innerText = ''; + } + + const chatDiv = document.querySelector('#chat'); + const setupDiv = document.querySelector('#setup'); + if (chatDiv && setupDiv) { + chatDiv.classList.add('hidden'); + setupDiv.classList.remove('hidden'); } } +function writeConfigToFile(path) { + fs.writeFileSync(path, JSON.stringify(config, null, 2)); +} + +function loadConfigFromFile() { + const configFile = fs.readFileSync("./config.json", 'utf8'); + config = JSON.parse(configFile); +} + +function renderGuildList() { + const guildList = document.querySelector('#guild-list'); + guildList.innerHTML = ''; + + for (const guildTopic in config.guilds) { + const guild = config.guilds[guildTopic]; + addGuildToList(guildTopic, guild.alias); + } +} + +async function connectToAllRooms() { + for (const guildTopic in config.guilds) { + const guild = config.guilds[guildTopic]; + for (const room of guild.rooms) { + await joinRoom(guildTopic, room.topic, room.alias); + } + } +} + +function toggleSetupView() { + const setupDiv = document.querySelector('#setup'); + const chatDiv = document.querySelector('#chat'); + setupDiv.classList.toggle('hidden'); + chatDiv.classList.toggle('hidden'); +} + +function truncateHash(hash) { + return `${hash.substring(0, 4)}...${hash.substring(hash.length - 4)}`; +} + +function updatePortInUrl(url) { + if (typeof url !== 'string') { + console.error('Invalid URL format:', url); + return ''; + } + const urlObj = new URL(url); + urlObj.port = servePort; + return urlObj.toString(); +} + +function addFileMessage(name, fileName, fileUrl, fileType, avatar, topic) { + const container = document.querySelector('#messages'); + if (!container) { + console.error('Element #messages not found'); + return; + } + + const messageDiv = document.createElement('div'); + messageDiv.classList.add('message'); + if (topic !== currentTopic()) { + messageDiv.classList.add('hidden'); // Hide messages not belonging to the current room + } + + const avatarImg = document.createElement('img'); + avatarImg.src = updatePortInUrl(avatar); + avatarImg.alt = `${name}'s avatar`; + avatarImg.classList.add('avatar'); + + const messageContent = document.createElement('div'); + messageContent.classList.add('message-content'); + + const senderName = document.createElement('div'); + senderName.classList.add('message-sender'); + senderName.textContent = name; + + const fileLink = document.createElement('a'); + fileLink.href = fileUrl; + fileLink.textContent = `File: ${fileName}`; + fileLink.classList.add('message-file'); + + if (fileType.startsWith('image/')) { + const filePreview = document.createElement('img'); + filePreview.src = fileUrl; + filePreview.alt = fileName; + filePreview.classList.add('file-preview'); + messageContent.appendChild(filePreview); + } else if (fileType.startsWith('video/')) { + const filePreview = document.createElement('video'); + filePreview.src = fileUrl; + filePreview.alt = fileName; + filePreview.classList.add('file-preview'); + filePreview.controls = true; + messageContent.appendChild(filePreview); + } else if (fileType.startsWith('audio/')) { + const filePreview = document.createElement('audio'); + filePreview.src = fileUrl; + filePreview.alt = fileName; + filePreview.classList.add('file-preview'); + filePreview.controls = true; + messageContent.appendChild(filePreview); + } else { + const filePreview = document.createElement('div'); + filePreview.textContent = 'No preview available'; + filePreview.classList.add('file-preview'); + messageContent.appendChild(filePreview); + } + + messageContent.appendChild(senderName); + messageContent.appendChild(fileLink); + messageDiv.appendChild(avatarImg); + messageDiv.appendChild(messageContent); + container.appendChild(messageDiv); + + if (topic === currentTopic()) { + container.scrollTop = container.scrollHeight; + } +} + +function addAudioMessage(name, audioUrl, avatar, topic) { + const container = document.querySelector('#messages'); + if (!container) { + console.error('Element #messages not found'); + return; + } + + const messageDiv = document.createElement('div'); + messageDiv.classList.add('message'); + if (topic !== currentTopic()) { + messageDiv.classList.add('hidden'); // Hide messages not belonging to the current room + } + + const avatarImg = document.createElement('img'); + avatarImg.src = updatePortInUrl(avatar); + avatarImg.alt = `${name}'s avatar`; + avatarImg.classList.add('avatar'); + + const messageContent = document.createElement('div'); + messageContent.classList.add('message-content'); + + const senderName = document.createElement('div'); + senderName.classList.add('message-sender'); + senderName.textContent = name; + + const audioElement = document.createElement('audio'); + audioElement.src = audioUrl; + audioElement.controls = true; + audioElement.classList.add('message-audio'); + + messageContent.appendChild(senderName); + messageContent.appendChild(audioElement); + messageDiv.appendChild(avatarImg); + messageDiv.appendChild(messageContent); + container.appendChild(messageDiv); + + if (topic === currentTopic()) { + container.scrollTop = container.scrollHeight; + } +} + +function addMessage(name, message, avatar, topic) { + const container = document.querySelector('#messages'); + if (!container) { + console.error('Element #messages not found'); + return; + } + + const messageDiv = document.createElement('div'); + messageDiv.classList.add('message'); + if (topic !== currentTopic()) { + messageDiv.classList.add('hidden'); // Hide messages not belonging to the current room + } + + const avatarImg = document.createElement('img'); + avatarImg.src = updatePortInUrl(avatar); + avatarImg.alt = `${name}'s avatar`; + avatarImg.classList.add('avatar'); + + const messageContent = document.createElement('div'); + messageContent.classList.add('message-content'); + + const senderName = document.createElement('div'); + senderName.classList.add('message-sender'); + senderName.textContent = name; + + const messageText = document.createElement('div'); + messageText.classList.add('message-text'); + messageText.innerHTML = message; + + messageContent.appendChild(senderName); + messageContent.appendChild(messageText); + messageDiv.appendChild(avatarImg); + messageDiv.appendChild(messageContent); + container.appendChild(messageDiv); + + if (topic === currentTopic()) { + container.scrollTop = container.scrollHeight; + } +} + +function clearMessages() { + const messagesContainer = document.querySelector('#messages'); + while (messagesContainer.firstChild) { + messagesContainer.removeChild(messagesContainer.firstChild); + } +} + +function currentTopic() { + return document.querySelector('#chat-room-topic').innerText; +} async function sendMessage(e) { e.preventDefault(); @@ -552,13 +1075,14 @@ async function sendMessage(e) { const topic = currentTopic(); const timestamp = Date.now(); + console.log("Sending message to current topic:", topic); // Add logging + if (message.startsWith('~')) { - // Handle command await handleCommand(message, { eventEmitter, - currentTopic, + currentTopic: topic, // Pass the current topic as a string clearMessages, - addMessage: (from, message, avatar, topic) => onMessageAdded(from, message, avatar, topic, timestamp), + addMessage, joinRoom, leaveRoom, createRoom, @@ -587,31 +1111,57 @@ async function sendMessage(e) { } } -async function handleFileInput(event) { +function onMessageAdded(name, message, avatar, topic, timestamp) { + if (!messagesStore[topic]) { + messagesStore[topic] = []; + } + messagesStore[topic].push({ name, message, avatar, timestamp }); + + const chatRoomTopic = currentTopic(); + if (topic === chatRoomTopic) { + addMessage(name, message, avatar, topic); + } +} + +function renderMessagesForRoom(topic) { + const container = document.querySelector('#messages'); + if (!container) { + console.error('Element #messages not found'); + return; + } + + container.innerHTML = ''; + + if (!messagesStore[topic]) return; + + messagesStore[topic].forEach(({ name, message, avatar }) => { + addMessage(name, message, avatar, topic); + }); +} + +function handleFileInput(event) { const file = event.target.files[0]; if (file) { const reader = new FileReader(); - reader.onload = async (event) => { + reader.onload = async function(event) { const buffer = new Uint8Array(event.target.result); const filePath = `/files/${file.name}`; await drive.put(filePath, buffer); - const fileUrl = `http://localhost:${servePort}${filePath}`; + const fileUrl = `http://localhost:${servePort}/files/${file.name}`; const topic = currentTopic(); const fileMessage = { type: 'file', name: config.userName, - avatar: updatePortInUrl(config.userAvatar), - topic: topic, - timestamp: Date.now(), fileName: file.name, file: b4a.toString(buffer, 'base64'), - fileType: file.type + fileType: file.type, + avatar: updatePortInUrl(config.userAvatar), + topic: topic, + timestamp: Date.now() }; - console.log('Sending file message:', fileMessage); // Debugging log - const peers = [...activeRooms.find(room => room.topic === topic).swarm.connections]; for (const peer of peers) { peer.write(JSON.stringify(fileMessage)); @@ -623,323 +1173,4 @@ async function handleFileInput(event) { } } -function addFileMessage(from, fileName, fileUrl, fileType, avatar, topic) { - console.log('Adding file message:', { from, fileName, fileUrl, fileType, avatar, topic }); // Debugging log - const $div = document.createElement('div'); - $div.classList.add('message'); - - const $img = document.createElement('img'); - $img.src = updatePortInUrl(avatar) || 'https://via.placeholder.com/40'; - $img.classList.add('avatar'); - $img.draggable = false; - $div.appendChild($img); - - const $content = document.createElement('div'); - $content.classList.add('message-content'); - - const $header = document.createElement('div'); - $header.classList.add('message-header'); - $header.textContent = from; - $content.appendChild($header); - - if (fileType.startsWith('image/')) { - const $image = document.createElement('img'); - $image.src = updatePortInUrl(fileUrl); - $image.alt = fileName; - $image.classList.add('attached-image'); - $content.appendChild($image); - } else { - const $fileButton = document.createElement('button'); - $fileButton.textContent = `Download File: ${fileName}`; - $fileButton.onclick = function() { - const $fileLink = document.createElement('a'); - $fileLink.href = fileUrl; - $fileLink.download = fileName; - $fileLink.click(); - }; - $content.appendChild($fileButton); - } - - $div.appendChild($content); - - // Only render the message if it's for the current room - if (currentTopic() === topic) { - document.querySelector('#messages').appendChild($div); - scrollToBottom(); - } else { - console.log(`Message for topic ${topic} not rendered because current topic is ${currentTopic()}`); // Debugging log - } -} - -function scrollToBottom() { - var container = document.getElementById("messages-container"); - container.scrollTop = container.scrollHeight; -} - -function onMessageAdded(from, message, avatar, topic, timestamp) { - console.log('Adding message:', { from, message, avatar, topic }); // Debugging log - const messageObj = { - from, - message, - avatar, - timestamp: timestamp || Date.now() - }; - - // Add the message to the store - addMessageToStore(topic, messageObj); - - // Only render messages for the current room - if (currentTopic() === topic) { - const $div = document.createElement('div'); - $div.classList.add('message'); - - const $img = document.createElement('img'); - $img.src = updatePortInUrl(avatar) || 'https://via.placeholder.com/40'; // Default to a placeholder image if avatar URL is not provided - $img.classList.add('avatar'); - $img.draggable = false; - $div.appendChild($img); - - const $content = document.createElement('div'); - $content.classList.add('message-content'); - - const $header = document.createElement('div'); - $header.classList.add('message-header'); - $header.textContent = from; - - const $text = document.createElement('div'); - $text.classList.add('message-text'); - - if (message.includes('Available files:')) { - const files = message.split('\n').slice(1); // Skip the "Available files:" line - const fileList = document.createElement('ul'); - - files.forEach(file => { - file = file.replace("- ", "") - const listItem = document.createElement('li'); - const fileButton = document.createElement('button'); - fileButton.textContent = file.trim(); - fileButton.onclick = () => downloadFile(file.trim()); - - const deleteButton = document.createElement('button'); - deleteButton.textContent = 'Delete'; - deleteButton.onclick = () => { - console.log("file to delete: ", file); - deleteFile(file); - listItem.remove(); - }; - - - listItem.appendChild(fileButton); - listItem.appendChild(deleteButton); - fileList.appendChild(listItem); - }); - - $text.appendChild(fileList); - } else { - - const md = window.markdownit({ - highlight: function (str, lang) { - if (lang && hljs.getLanguage(lang)) { - try { - return hljs.highlight(str, { language: lang }).value; - } catch (__) {} - } - return ''; // use external default escaping - } - }); - const markdownContent = md.render(message); - $text.innerHTML = markdownContent; - } - - $content.appendChild($header); - $content.appendChild($text); - $div.appendChild($content); - - document.querySelector('#messages').appendChild($div); - scrollToBottom(); - } else { - console.log(`Message for topic ${topic} not rendered because current topic is ${currentTopic()}`); // Debugging log - } -} - -function downloadFile(filename) { - const fileUrl = `http://localhost:${servePort}/files/${filename}`; - const a = document.createElement('a'); - a.href = fileUrl; - a.download = filename; - a.click(); -} - - -function addAudioMessage(from, audioUrl, avatar, topic) { - console.log('Adding audio message:', { from, audioUrl, avatar, topic }); // Debugging log - const $div = document.createElement('div'); - $div.classList.add('message'); - - const $img = document.createElement('img'); - $img.src = updatePortInUrl(avatar) || 'https://via.placeholder.com/40'; - $img.classList.add('avatar'); - $img.draggable = false; - $div.appendChild($img); - - const $content = document.createElement('div'); - $content.classList.add('message-content'); - - const $header = document.createElement('div'); - $header.classList.add('message-header'); - $header.textContent = from; - $content.appendChild($header); - - const $audio = document.createElement('audio'); - $audio.controls = true; - if (from !== config.userName) { - $audio.autoplay = true; // Add autoplay attribute for peers only - } - $audio.src = updatePortInUrl(audioUrl); - $audio.classList.add('attached-audio'); - $content.appendChild($audio); - - $div.appendChild($content); - - // Only render the message if it's for the current room - if (currentTopic() === topic) { - document.querySelector('#messages').appendChild($div); - scrollToBottom(); - } else { - console.log(`Message for topic ${topic} not rendered because current topic is ${currentTopic()}`); // Debugging log - } -} - -function truncateHash(hash) { - return `${hash.slice(0, 6)}...${hash.slice(-6)}`; -} - -async function updateIcon(username, avatarBuffer) { - const userIcon = document.querySelector(`img[src*="${username}.png"]`); - if (userIcon) { - const avatarBlob = new Blob([avatarBuffer], { type: 'image/png' }); - const avatarUrl = URL.createObjectURL(avatarBlob); - userIcon.src = updatePortInUrl(avatarUrl); - - config.userAvatar = avatarUrl; - writeConfigToFile("./config.json"); - } -} - -function clearMessagesCMD() { - const messagesContainer = document.querySelector('#messages'); - while (messagesContainer.firstChild) { - messagesContainer.removeChild(messagesContainer.firstChild); - } - - // Clear the messages from the store for the current room - const topic = currentTopic(); - messagesStore[topic] = []; -} - -function clearMessages() { - const messagesContainer = document.querySelector('#messages'); - while (messagesContainer.firstChild) { - messagesContainer.removeChild(messagesContainer.firstChild); - } - - // Clear the messages from the store for the current room - // const topic = currentTopic(); - // messagesStore[topic] = []; -} - -function toggleSetupView() { - const setupDiv = document.querySelector('#setup'); - setupDiv.classList.toggle('hidden'); -} - -function writeConfigToFile(filePath) { - fs.writeFile(filePath, JSON.stringify(config), (err) => { - if (err) return console.error(err); - console.log("File has been created"); - }); -} - -function updatePortInUrl(url) { - if (!url) return url; - const urlObject = new URL(url); - if(!urlObject.host.startsWith("localhost")) return urlObject.toString(); - urlObject.port = servePort; - return urlObject.toString(); -} - -function renderRoomList() { - const roomList = document.querySelector('#room-list'); - roomList.innerHTML = ''; - - config.rooms.forEach(room => { - const roomItem = document.createElement('li'); - roomItem.textContent = room.alias || truncateHash(room.topic); - roomItem.dataset.topic = room.topic; - - roomItem.addEventListener('dblclick', () => enterEditMode(roomItem)); - roomItem.addEventListener('click', () => switchRoom(room.topic)); - roomList.appendChild(roomItem); - }); -} - -function renderMessagesForRoom(topic) { - console.log('Rendering messages for room:', topic); // Debugging log - // Clear the message area - clearMessages(); - - // Fetch and render messages for the selected room - const messages = getMessagesForRoom(topic); - messages.forEach(message => { - onMessageAdded(message.from, message.message, message.avatar, topic, message.timestamp); - }); -} - -function getMessagesForRoom(topic) { - return messagesStore[topic] || []; -} - -function addMessageToStore(topic, messageObj) { - if (!messagesStore[topic]) { - messagesStore[topic] = []; - } - - // Check for duplicates using a combination of message content and timestamp - const isDuplicate = messagesStore[topic].some(msg => - msg.from === messageObj.from && - msg.message === messageObj.message && - msg.timestamp === messageObj.timestamp - ); - - if (!isDuplicate) { - messagesStore[topic].push(messageObj); - } else { - console.log('Duplicate message detected:', messageObj); // Debugging log - } -} - -function loadConfigFromFile() { - config = JSON.parse(fs.readFileSync("./config.json", 'utf8')); - console.log("Read config from file:", config); - // Update port in URLs - config.userAvatar = updatePortInUrl(config.userAvatar); - config.rooms.forEach(room => { - room.alias = room.alias || truncateHash(room.topic); - }); - for (let user in config.registeredUsers) { - config.registeredUsers[user] = updatePortInUrl(config.registeredUsers[user]); - } -} - -async function connectToAllRooms() { - // Connect to all rooms on startup - for (const room of config.rooms) { - const topicBuffer = b4a.from(room.topic, 'hex'); - await joinSwarm(topicBuffer); - } -} - -// Call this function when loading the rooms initially -renderRoomList(); - initialize(); diff --git a/chatBot/commands/ping.js b/chatBot/commands/ping.js index 8ba0a84..d7566cd 100644 --- a/chatBot/commands/ping.js +++ b/chatBot/commands/ping.js @@ -1,7 +1,7 @@ export default { handler: function(bot, args, message) { // Specify the path to the file you want to send - const filePath = '/to/file/path.js'; // Replace with the actual file path + const filePath = '/Users/raven/chat/chatBot/bot.js'; // Replace with the actual file path const fileType = 'text/html'; // Specify the correct file type // Send the file message using the bot instance diff --git a/commands.js b/commands.js index 2d6b26f..3c2241c 100644 --- a/commands.js +++ b/commands.js @@ -1,4 +1,3 @@ -// commands.js import b4a from 'b4a'; import fs from 'fs'; @@ -13,44 +12,50 @@ if (fs.existsSync(agentAvatarPath)) { export default async function handleCommand(command, context) { const { eventEmitter, currentTopic, clearMessages, addMessage, joinRoom, leaveRoom, createRoom, listFiles } = context; - + + console.log("Context received in handleCommand:", context); // Add logging + const args = command.trim().split(' '); const cmd = args[0].toLowerCase(); const restArgs = args.slice(1).join(' '); + console.log("Command received:", cmd); // Add logging + console.log("Current topic:", currentTopic); // Add logging to check the current topic + switch (cmd) { case '~clear': - clearMessages(); + clearMessages(currentTopic); break; case '~ping': - addMessage('LinkUp', 'pong', agentAvatar, currentTopic()); + addMessage('LinkUp', 'pong', agentAvatar, currentTopic); break; case '~help': - addMessage('LinkUp', 'Available commands:\n- ~clear\n- ~ping\n- ~help\n- ~join [topic]\n- ~leave\n- ~create [alias]\n- ~list-files', agentAvatar, currentTopic()); + addMessage('LinkUp', 'Available commands:\n- ~clear\n- ~ping\n- ~help\n- ~join [topic]\n- ~leave\n- ~create [alias]\n- ~list-files', agentAvatar, currentTopic); break; case '~join': if (restArgs) { - await joinRoom(restArgs); + await joinRoom(currentTopic, restArgs); } else { - addMessage('LinkUp', 'Usage: ~join [topic]', agentAvatar, currentTopic()); + addMessage('LinkUp', 'Usage: ~join [topic]', agentAvatar, currentTopic); } break; case '~leave': - leaveRoom(currentTopic()); + leaveRoom(currentTopic); break; case '~create': if (restArgs) { - await createRoom(restArgs); + await createRoom(currentTopic, restArgs); } else { - addMessage('LinkUp', 'Usage: ~create [alias]', agentAvatar, currentTopic()); + addMessage('LinkUp', 'Usage: ~create [alias]', agentAvatar, currentTopic); } break; case '~list-files': const files = await listFiles(); const fileList = files.length > 0 ? files.map(file => `- ${file}`).join('\n') : 'No files available'; - addMessage('LinkUp', `Available files:\n${fileList}`, agentAvatar, currentTopic()); + addMessage('LinkUp', `Available files:\n${fileList}`, agentAvatar, currentTopic); break; default: + addMessage('LinkUp', `Unknown command: ${cmd}`, agentAvatar, currentTopic); console.log('Unknown command:', command); } } diff --git a/index.html b/index.html index d918404..8f539cb 100644 --- a/index.html +++ b/index.html @@ -17,10 +17,10 @@
@@ -62,7 +61,7 @@ - + @@ -70,11 +69,46 @@
+ + + + + + + - + \ No newline at end of file diff --git a/style.css b/style.css index a48d1e1..9e76c3c 100644 --- a/style.css +++ b/style.css @@ -281,7 +281,6 @@ textarea::placeholder { background-color: #f04747; } - /* Main container styles */ main { display: flex; @@ -474,78 +473,146 @@ main { } /* Updated Room List Styles */ -#room-list { +#guild-list { list-style: none; padding: 0; margin: 0; } -#room-list li { +.guild-item { padding: 10px; margin-bottom: 10px; background-color: #3a3f44; border-radius: 5px; cursor: pointer; display: flex; + flex-direction: column; + transition: background-color 0.3s ease; +} + +.guild-item:hover { + background-color: #4a5258; +} + +.guild-item h3 { + margin: 0; + font-size: 16px; + color: #ffffff; +} + +.guild-item .manage-guild-btn { + align-self: flex-end; + margin-top: 10px; + font-size: 12px; + padding: 5px 10px; +} + +.guild-item .room-list { + list-style: none; + padding: 0; + margin: 10px 0 0 0; + display: flex; + flex-direction: column; +} + +.guild-item .room-list li { + padding: 5px; + margin-bottom: 5px; + background-color: #464343; + border-radius: 3px; + cursor: pointer; + display: flex; justify-content: space-between; align-items: center; transition: background-color 0.3s ease; } -#room-list li:hover { - background-color: #4a5258; +.guild-item .room-list li:hover { + background-color: #5a5f64; } -#room-list li span { +.guild-item .room-list li span { flex: 1; } -#room-list li .edit-icon, -#room-list li .delete-icon { +.guild-item .room-list li .edit-icon, +.guild-item .room-list li .delete-icon { margin-left: 10px; color: #e0e0e0; cursor: pointer; transition: color 0.3s ease; } -#room-list li .edit-icon:hover, -#room-list li .delete-icon:hover { +.guild-item .room-list li .edit-icon:hover, +.guild-item .room-list li .delete-icon:hover { color: #a0a0a0; } -/* Style for Edit Mode Input Box */ -#room-list li input[type="text"] { +/* Modal styles */ +.modal { + display: flex; + justify-content: center; + align-items: center; + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.8); + z-index: 1000; +} + +.modal-content { background-color: #2e2e2e; - border: 1px solid #464343; - border-radius: 5px; + padding: 1.5rem; + border-radius: 8px; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.5); + max-width: 500px; + width: 100%; + text-align: center; +} + +.close-btn { color: #e0e0e0; - padding: 5px; - width: calc(100% - 40px); - margin-right: 10px; - transition: background-color 0.3s ease, color 0.3s ease, border-color 0.3s ease; + float: right; + font-size: 1.5rem; + cursor: pointer; } -#room-list li input[type="text"]:focus { - outline: none; - background-color: #3a3a3a; +.close-btn:hover { + color: #f04747; } -/* Link styles */ -a { - color: #e0e0e0; /* Base color for links matching the text color */ - text-decoration: none; /* Remove underline */ - transition: color 0.3s ease, text-decoration 0.3s ease; +.modal h2 { + margin-bottom: 1rem; + color: #fff; } -a:hover { - color: #b0b0b0; /* Lighter color on hover */ - text-decoration: underline; /* Underline on hover */ +.modal form { + display: flex; + flex-direction: column; + align-items: center; } -a:active { - color: #a0a0a0; /* Slightly darker color on active */ +.modal form input, +.modal form button { + margin-bottom: 1rem; + width: 100%; } -a:visited { - color: #b0b0b0; /* Different color for visited links */ +.modal form button { + width: auto; + background-color: #3e3e3e; + color: #fff; + border: none; + padding: 0.75rem 1.5rem; + border-radius: 5px; + cursor: pointer; + transition: background-color 0.3s ease, transform 0.3s ease; } + +.modal form button:hover { + background-color: #191919; + transform: scale(1.05); +} + From fd02cff23f6a3bbcde1793c3eab37e67adde38c4 Mon Sep 17 00:00:00 2001 From: Raven Scott Date: Sat, 22 Jun 2024 16:49:42 -0400 Subject: [PATCH 02/20] test --- app.js | 38 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/app.js b/app.js index 72dcc80..d479cad 100644 --- a/app.js +++ b/app.js @@ -339,7 +339,15 @@ function handleIncomingMessage(messageObj) { } } } - } else { + } else if (messageObj.type === 'guildJoin') { + const { guildTopic, guildData } = messageObj; + if (!config.guilds[guildTopic]) { + config.guilds[guildTopic] = guildData; + writeConfigToFile("./config.json"); + renderGuildList(); + joinGuild(guildTopic); + } + } else { console.error('Received unknown message type:', messageObj); } } @@ -380,7 +388,19 @@ async function handleConnection(connection, info) { connection.on('data', (data) => { const messageObj = JSON.parse(data.toString()); - eventEmitter.emit('onMessage', messageObj); + if (messageObj.type === 'guildJoin') { + const guildData = config.guilds[messageObj.guildTopic]; + if (guildData) { + const guildJoinMessage = JSON.stringify({ + type: 'guildJoin', + guildTopic: messageObj.guildTopic, + guildData: guildData + }); + connection.write(guildJoinMessage); + } + } else { + eventEmitter.emit('onMessage', messageObj); + } }); connection.on('close', () => { @@ -555,6 +575,20 @@ async function joinChatRoom(e) { const roomTopicBuffer = b4a.from(roomTopic, 'hex'); addRoomToList(guildTopic, roomTopic); await joinSwarm(roomTopicBuffer); + + // Fetch guild data from a peer + const guildJoinMessage = JSON.stringify({ + type: 'guildJoin', + guildTopic: guildTopic + }); + + const guildSwarm = activeRooms.find(room => room.topic === guildTopic); + if (guildSwarm) { + const peers = [...guildSwarm.swarm.connections]; + for (const peer of peers) { + peer.write(guildJoinMessage); + } + } } async function joinSwarm(topicBuffer) { From 6ea37b8082b76277aed24cab113cb04256e198fe Mon Sep 17 00:00:00 2001 From: Raven Scott Date: Sat, 22 Jun 2024 16:52:05 -0400 Subject: [PATCH 03/20] test --- app.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/app.js b/app.js index d479cad..d0aea41 100644 --- a/app.js +++ b/app.js @@ -82,6 +82,18 @@ async function joinGuild(guildTopic) { } } +async function processGuild(guildData) { + const parsedData = JSON.parse(guildData); + config.guilds[parsedData.guildTopic] = { + alias: parsedData.guildAlias, + rooms: parsedData.rooms, + owner: parsedData.owner + }; + writeConfigToFile("./config.json"); + renderGuildList(); + await joinGuild(parsedData.guildTopic); +} + async function joinRoom(guildTopic, roomTopic, alias) { const topicBuffer = b4a.from(roomTopic, 'hex'); addRoomToList(guildTopic, roomTopic, alias); From 0e6d074c11ffca1c2a9bf802dc5e28e12a78d8cb Mon Sep 17 00:00:00 2001 From: Raven Scott Date: Sat, 22 Jun 2024 16:53:07 -0400 Subject: [PATCH 04/20] test --- app.js | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/app.js b/app.js index d0aea41..d479cad 100644 --- a/app.js +++ b/app.js @@ -82,18 +82,6 @@ async function joinGuild(guildTopic) { } } -async function processGuild(guildData) { - const parsedData = JSON.parse(guildData); - config.guilds[parsedData.guildTopic] = { - alias: parsedData.guildAlias, - rooms: parsedData.rooms, - owner: parsedData.owner - }; - writeConfigToFile("./config.json"); - renderGuildList(); - await joinGuild(parsedData.guildTopic); -} - async function joinRoom(guildTopic, roomTopic, alias) { const topicBuffer = b4a.from(roomTopic, 'hex'); addRoomToList(guildTopic, roomTopic, alias); From 8c0f7ebd0fb1d00eccc16c283dcab498a1b58d5b Mon Sep 17 00:00:00 2001 From: Raven Scott Date: Sat, 22 Jun 2024 16:56:04 -0400 Subject: [PATCH 05/20] test --- app.js | 26 +++++++++++++++++++------- index.html | 6 ++++-- 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/app.js b/app.js index d479cad..b7e1383 100644 --- a/app.js +++ b/app.js @@ -61,18 +61,30 @@ function updatePeerCount() { } } -async function processGuild(guildData) { - const parsedData = JSON.parse(guildData); - config.guilds[parsedData.guildTopic] = { - alias: parsedData.guildAlias, - rooms: parsedData.rooms, - owner: parsedData.owner +async function processGuild(guildTopic) { + // Simulate fetching guild data for the given topic + const guildData = { + guildTopic, + guildAlias: "Sample Guild", + rooms: [ + { topic: "room1", alias: "Room 1" }, + { topic: "room2", alias: "Room 2" } + ], + owner: config.userName + }; + + config.guilds[guildData.guildTopic] = { + alias: guildData.guildAlias, + rooms: guildData.rooms, + owner: guildData.owner }; writeConfigToFile("./config.json"); renderGuildList(); - await joinGuild(parsedData.guildTopic); + await joinGuild(guildData.guildTopic); } +export { processGuild }; + async function joinGuild(guildTopic) { const guild = config.guilds[guildTopic]; if (guild) { diff --git a/index.html b/index.html index 8f539cb..51f79ce 100644 --- a/index.html +++ b/index.html @@ -103,7 +103,9 @@ - - \ No newline at end of file + From de9e01e93228bee09896f2db739018d35cfbbb4e Mon Sep 17 00:00:00 2001 From: snxraven Date: Sat, 22 Jun 2024 21:19:28 +0000 Subject: [PATCH 06/20] revert 8c0f7ebd0fb1d00eccc16c283dcab498a1b58d5b revert test --- app.js | 26 +++++++------------------- index.html | 6 ++---- 2 files changed, 9 insertions(+), 23 deletions(-) diff --git a/app.js b/app.js index b7e1383..d479cad 100644 --- a/app.js +++ b/app.js @@ -61,30 +61,18 @@ function updatePeerCount() { } } -async function processGuild(guildTopic) { - // Simulate fetching guild data for the given topic - const guildData = { - guildTopic, - guildAlias: "Sample Guild", - rooms: [ - { topic: "room1", alias: "Room 1" }, - { topic: "room2", alias: "Room 2" } - ], - owner: config.userName - }; - - config.guilds[guildData.guildTopic] = { - alias: guildData.guildAlias, - rooms: guildData.rooms, - owner: guildData.owner +async function processGuild(guildData) { + const parsedData = JSON.parse(guildData); + config.guilds[parsedData.guildTopic] = { + alias: parsedData.guildAlias, + rooms: parsedData.rooms, + owner: parsedData.owner }; writeConfigToFile("./config.json"); renderGuildList(); - await joinGuild(guildData.guildTopic); + await joinGuild(parsedData.guildTopic); } -export { processGuild }; - async function joinGuild(guildTopic) { const guild = config.guilds[guildTopic]; if (guild) { diff --git a/index.html b/index.html index 51f79ce..8f539cb 100644 --- a/index.html +++ b/index.html @@ -103,9 +103,7 @@ - - + \ No newline at end of file From a1b11f7ae65ff33f575d041e5130fb5e684065b5 Mon Sep 17 00:00:00 2001 From: Raven Scott Date: Sun, 7 Jul 2024 20:46:59 -0400 Subject: [PATCH 07/20] test --- app.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/app.js b/app.js index b7e1383..9c3d5d7 100644 --- a/app.js +++ b/app.js @@ -63,6 +63,8 @@ function updatePeerCount() { async function processGuild(guildTopic) { // Simulate fetching guild data for the given topic + console.log('Processing guild for topic:', guildTopic); // Debugging log + const guildData = { guildTopic, guildAlias: "Sample Guild", @@ -207,6 +209,7 @@ function setupEventListeners() { const attachFileButton = document.getElementById('attach-file'); const fileInput = document.getElementById('file-input'); const talkButton = document.getElementById('talk-btn'); + const joinGuildBtn = document.getElementById('join-guild'); if (registerForm) { registerForm.addEventListener('submit', registerUser); @@ -246,6 +249,17 @@ function setupEventListeners() { if (talkButton) { setupTalkButton(); } + if (joinGuildBtn) { + joinGuildBtn.addEventListener('click', async (event) => { + const guildTopic = document.getElementById('join-guild-topic').value.trim(); + if (guildTopic) { + console.log('Join Guild button clicked with topic:', guildTopic); // Debugging log + await processGuild(guildTopic); + } else { + console.error('Guild topic is empty'); + } + }); + } // Add event listeners only for room items document.querySelectorAll('.room-item').forEach(item => { From da1bf28e3d240838f61a5e440bb5a74f9d5d85ee Mon Sep 17 00:00:00 2001 From: Raven Scott Date: Sun, 7 Jul 2024 20:51:12 -0400 Subject: [PATCH 08/20] test --- app.js | 42 ++++++++---------------------------------- 1 file changed, 8 insertions(+), 34 deletions(-) diff --git a/app.js b/app.js index 9c3d5d7..d795b4d 100644 --- a/app.js +++ b/app.js @@ -61,32 +61,18 @@ function updatePeerCount() { } } -async function processGuild(guildTopic) { - // Simulate fetching guild data for the given topic - console.log('Processing guild for topic:', guildTopic); // Debugging log - - const guildData = { - guildTopic, - guildAlias: "Sample Guild", - rooms: [ - { topic: "room1", alias: "Room 1" }, - { topic: "room2", alias: "Room 2" } - ], - owner: config.userName - }; - - config.guilds[guildData.guildTopic] = { - alias: guildData.guildAlias, - rooms: guildData.rooms, - owner: guildData.owner +async function processGuild(guildData) { + const parsedData = JSON.parse(guildData); + config.guilds[parsedData.guildTopic] = { + alias: parsedData.guildAlias, + rooms: parsedData.rooms, + owner: parsedData.owner }; writeConfigToFile("./config.json"); renderGuildList(); - await joinGuild(guildData.guildTopic); + await joinGuild(parsedData.guildTopic); } -export { processGuild }; - async function joinGuild(guildTopic) { const guild = config.guilds[guildTopic]; if (guild) { @@ -209,7 +195,6 @@ function setupEventListeners() { const attachFileButton = document.getElementById('attach-file'); const fileInput = document.getElementById('file-input'); const talkButton = document.getElementById('talk-btn'); - const joinGuildBtn = document.getElementById('join-guild'); if (registerForm) { registerForm.addEventListener('submit', registerUser); @@ -249,17 +234,6 @@ function setupEventListeners() { if (talkButton) { setupTalkButton(); } - if (joinGuildBtn) { - joinGuildBtn.addEventListener('click', async (event) => { - const guildTopic = document.getElementById('join-guild-topic').value.trim(); - if (guildTopic) { - console.log('Join Guild button clicked with topic:', guildTopic); // Debugging log - await processGuild(guildTopic); - } else { - console.error('Guild topic is empty'); - } - }); - } // Add event listeners only for room items document.querySelectorAll('.room-item').forEach(item => { @@ -1233,4 +1207,4 @@ function handleFileInput(event) { } } -initialize(); +initialize(); \ No newline at end of file From 181d011b8d41ab8e1e50bd07ece22032300c3d7c Mon Sep 17 00:00:00 2001 From: Raven Scott Date: Sun, 7 Jul 2024 21:08:30 -0400 Subject: [PATCH 09/20] more test --- app.js | 45 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/app.js b/app.js index d795b4d..64a846c 100644 --- a/app.js +++ b/app.js @@ -179,6 +179,11 @@ async function initialize() { console.log('Event switchRoom with roomTopic:', roomTopic); switchRoom(guildTopic, roomTopic); }); + + document.addEventListener('joinGuildRequest', (event) => { + const { guildTopic } = event.detail; + joinGuildRequest(guildTopic); + }); } catch (error) { console.error('Error during initialization:', error); } @@ -195,6 +200,7 @@ function setupEventListeners() { const attachFileButton = document.getElementById('attach-file'); const fileInput = document.getElementById('file-input'); const talkButton = document.getElementById('talk-btn'); + const joinGuildBtn = document.getElementById('join-guild'); if (registerForm) { registerForm.addEventListener('submit', registerUser); @@ -234,6 +240,14 @@ function setupEventListeners() { if (talkButton) { setupTalkButton(); } + if (joinGuildBtn) { + joinGuildBtn.addEventListener('click', () => { + const guildTopic = document.getElementById('join-guild-topic').value.trim(); + if (guildTopic) { + joinGuildRequest(guildTopic); + } + }); + } // Add event listeners only for room items document.querySelectorAll('.room-item').forEach(item => { @@ -1207,4 +1221,33 @@ function handleFileInput(event) { } } -initialize(); \ No newline at end of file +async function joinGuildRequest(guildTopic) { + const guildTopicBuffer = b4a.from(guildTopic, 'hex'); + if (!activeRooms.some(room => room.topic === guildTopic)) { + try { + const swarm = new Hyperswarm(); + const discovery = swarm.join(guildTopicBuffer, { client: true, server: true }); + await discovery.flushed(); + + swarm.on('connection', (connection, info) => { + handleConnection(connection, info); + // Request guild information from peers + const guildRequestMessage = JSON.stringify({ + type: 'guildRequest', + guildTopic + }); + connection.write(guildRequestMessage); + }); + + activeRooms.push({ topic: guildTopic, swarm, discovery }); + + console.log('Joined guild topic:', guildTopic); // Debugging log + + updatePeerCount(); + } catch (error) { + console.error('Error joining swarm for guild topic:', guildTopic, error); + } + } +} + +initialize(); From d423031cfbf544aa8a95f110b2434b6f21824a2c Mon Sep 17 00:00:00 2001 From: Raven Scott Date: Sun, 7 Jul 2024 21:14:17 -0400 Subject: [PATCH 10/20] more test --- app.js | 19 ++++++++++++++++++- index.html | 4 ++-- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/app.js b/app.js index 64a846c..83dacd1 100644 --- a/app.js +++ b/app.js @@ -351,6 +351,8 @@ function handleIncomingMessage(messageObj) { peer.write(renameMessage); } } + + } } } else if (messageObj.type === 'guildJoin') { @@ -361,7 +363,22 @@ function handleIncomingMessage(messageObj) { renderGuildList(); joinGuild(guildTopic); } - } else { + } else if (messageObj.type === 'guildRequest') { + const guildTopic = messageObj.guildTopic; + const guild = config.guilds[guildTopic]; + if (guild) { + const guildResponseMessage = JSON.stringify({ + type: 'guildResponse', + guildData: JSON.stringify({ + guildTopic, + guildAlias: guild.alias, + rooms: guild.rooms, + owner: guild.owner + }) + }); + connection.write(guildResponseMessage); + } + } else { console.error('Received unknown message type:', messageObj); } } diff --git a/index.html b/index.html index 8f539cb..6d89123 100644 --- a/index.html +++ b/index.html @@ -185,7 +185,7 @@ joinGuildBtn.addEventListener('click', async (event) => { const guildTopic = document.getElementById('join-guild-topic').value.trim(); if (guildTopic) { - await processGuild(guildTopic); + await joinGuildRequest(guildTopic); } }); @@ -254,4 +254,4 @@ } - \ No newline at end of file + From e0ad9c7067a7af26f058c7a80d361fd0c027e53c Mon Sep 17 00:00:00 2001 From: Raven Scott Date: Sun, 7 Jul 2024 21:21:55 -0400 Subject: [PATCH 11/20] more test --- app.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app.js b/app.js index 83dacd1..1eafee8 100644 --- a/app.js +++ b/app.js @@ -351,8 +351,6 @@ function handleIncomingMessage(messageObj) { peer.write(renameMessage); } } - - } } } else if (messageObj.type === 'guildJoin') { @@ -1267,4 +1265,6 @@ async function joinGuildRequest(guildTopic) { } } +window.joinGuildRequest = joinGuildRequest; + initialize(); From 55875f468c5e04607a5e8398b6f9e9175606d1e6 Mon Sep 17 00:00:00 2001 From: Raven Scott Date: Sun, 7 Jul 2024 21:30:09 -0400 Subject: [PATCH 12/20] more test --- app.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app.js b/app.js index 1eafee8..8e93c44 100644 --- a/app.js +++ b/app.js @@ -263,7 +263,7 @@ function setupEventListeners() { }); } -function handleIncomingMessage(messageObj) { +function handleIncomingMessage(messageObj, connection) { console.log('Received message:', messageObj); // Debugging log if (messageObj.type === 'icon') { @@ -376,6 +376,9 @@ function handleIncomingMessage(messageObj) { }); connection.write(guildResponseMessage); } + } else if (messageObj.type === 'guildResponse') { + const guildData = messageObj.guildData; + processGuild(guildData); } else { console.error('Received unknown message type:', messageObj); } @@ -428,7 +431,7 @@ async function handleConnection(connection, info) { connection.write(guildJoinMessage); } } else { - eventEmitter.emit('onMessage', messageObj); + eventEmitter.emit('onMessage', messageObj, connection); } }); From ed4625275dd0921267ca311d344ce69ce52274b4 Mon Sep 17 00:00:00 2001 From: Raven Scott Date: Sun, 7 Jul 2024 21:40:52 -0400 Subject: [PATCH 13/20] init all guild topics on start --- app.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/app.js b/app.js index 8e93c44..a5974f5 100644 --- a/app.js +++ b/app.js @@ -141,6 +141,7 @@ async function initialize() { loadConfigFromFile(); renderGuildList(); await connectToAllRooms(); + await joinAllGuilds(); // Ensure the app joins all guilds on startup } if (!configExists) { @@ -189,6 +190,12 @@ async function initialize() { } } +async function joinAllGuilds() { + for (const guildTopic in config.guilds) { + await joinGuildRequest(guildTopic); + } +} + function setupEventListeners() { const registerForm = document.querySelector('#register-form'); const selectAvatarButton = document.querySelector('#select-avatar'); From 43e9890235383679d29f86b76139476f35a3d25f Mon Sep 17 00:00:00 2001 From: Raven Scott Date: Sun, 7 Jul 2024 21:49:13 -0400 Subject: [PATCH 14/20] readd updateIcon --- app.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/app.js b/app.js index a5974f5..954bc6b 100644 --- a/app.js +++ b/app.js @@ -1129,6 +1129,19 @@ function addMessage(name, message, avatar, topic) { } } +async function updateIcon(username, avatarBuffer) { + const userIcon = document.querySelector(`img[src*="${username}.png"]`); + if (userIcon) { + const avatarBlob = new Blob([avatarBuffer], { type: 'image/png' }); + const avatarUrl = URL.createObjectURL(avatarBlob); + userIcon.src = updatePortInUrl(avatarUrl); + + config.userAvatar = avatarUrl; + writeConfigToFile("./config.json"); + } +} + + function clearMessages() { const messagesContainer = document.querySelector('#messages'); while (messagesContainer.firstChild) { From a0c7eb1561de030a8a13d6140926e7c60ea80c5f Mon Sep 17 00:00:00 2001 From: Raven Scott Date: Sun, 7 Jul 2024 21:52:57 -0400 Subject: [PATCH 15/20] fix --- app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app.js b/app.js index 954bc6b..270db99 100644 --- a/app.js +++ b/app.js @@ -1185,7 +1185,7 @@ async function sendMessage(e) { const messageObj = JSON.stringify({ type: 'message', name: config.userName, - avatar: config.userAvatar, + avatar: updatePortInUrl(config.userAvatar), topic: topic, timestamp: timestamp, message From 084cb02a11c9dd17d6803879e9a3a84534c0ab49 Mon Sep 17 00:00:00 2001 From: Raven Scott Date: Sun, 7 Jul 2024 22:00:13 -0400 Subject: [PATCH 16/20] fix icon message --- app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app.js b/app.js index 270db99..793e64d 100644 --- a/app.js +++ b/app.js @@ -399,7 +399,7 @@ async function handleConnection(connection, info) { if (iconBuffer) { const iconMessage = JSON.stringify({ type: 'icon', - username: config.userName, + name: config.userName, avatar: b4a.toString(iconBuffer, 'base64'), timestamp: Date.now() }); From c530a8d69c387661865461c1671b28da80cbbb05 Mon Sep 17 00:00:00 2001 From: Raven Scott Date: Sun, 7 Jul 2024 22:39:31 -0400 Subject: [PATCH 17/20] Add back file menu --- app.js | 5 +++-- commands.js | 45 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 47 insertions(+), 3 deletions(-) diff --git a/app.js b/app.js index 793e64d..13b4f60 100644 --- a/app.js +++ b/app.js @@ -1116,6 +1116,7 @@ function addMessage(name, message, avatar, topic) { const messageText = document.createElement('div'); messageText.classList.add('message-text'); + messageText.innerHTML = message; messageContent.appendChild(senderName); @@ -1162,7 +1163,6 @@ async function sendMessage(e) { const timestamp = Date.now(); console.log("Sending message to current topic:", topic); // Add logging - if (message.startsWith('~')) { await handleCommand(message, { eventEmitter, @@ -1173,7 +1173,8 @@ async function sendMessage(e) { leaveRoom, createRoom, listFiles, - deleteFile + deleteFile, + servePort }); return; } diff --git a/commands.js b/commands.js index 3c2241c..912c9cc 100644 --- a/commands.js +++ b/commands.js @@ -11,7 +11,7 @@ if (fs.existsSync(agentAvatarPath)) { } export default async function handleCommand(command, context) { - const { eventEmitter, currentTopic, clearMessages, addMessage, joinRoom, leaveRoom, createRoom, listFiles } = context; + const { eventEmitter, currentTopic, clearMessages, addMessage, joinRoom, leaveRoom, createRoom, listFiles, deleteFile, servePort } = context; console.log("Context received in handleCommand:", context); // Add logging @@ -53,9 +53,52 @@ export default async function handleCommand(command, context) { const files = await listFiles(); const fileList = files.length > 0 ? files.map(file => `- ${file}`).join('\n') : 'No files available'; addMessage('LinkUp', `Available files:\n${fileList}`, agentAvatar, currentTopic); + + // Render the file list with delete buttons + renderFileList(files, deleteFile, servePort); break; default: addMessage('LinkUp', `Unknown command: ${cmd}`, agentAvatar, currentTopic); console.log('Unknown command:', command); } } + +function renderFileList(files, deleteFile, servePort) { + const container = document.querySelector('#messages'); + if (!container) { + console.error('Element #messages not found'); + return; + } + + const fileList = document.createElement('ul'); + + files.forEach(file => { + const listItem = document.createElement('li'); + const fileButton = document.createElement('button'); + fileButton.textContent = file.trim(); + fileButton.onclick = () => downloadFile(file.trim(), servePort); + + const deleteButton = document.createElement('button'); + deleteButton.textContent = 'Delete'; + deleteButton.onclick = async () => { + await deleteFile(file); + listItem.remove(); + }; + + listItem.appendChild(fileButton); + listItem.appendChild(deleteButton); + fileList.appendChild(listItem); + }); + + container.appendChild(fileList); +} + +function downloadFile(filename, servePort) { + const url = `http://localhost:${servePort}/files/${filename}`; + const link = document.createElement('a'); + link.href = url; + link.download = filename; + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); +} From aa70e42fdcf40019cf5cbc921501b2d463cb0d7a Mon Sep 17 00:00:00 2001 From: Raven Scott Date: Sun, 7 Jul 2024 22:40:22 -0400 Subject: [PATCH 18/20] change internal agent prefix --- app.js | 2 +- commands.js | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/app.js b/app.js index 13b4f60..cd2ec92 100644 --- a/app.js +++ b/app.js @@ -1163,7 +1163,7 @@ async function sendMessage(e) { const timestamp = Date.now(); console.log("Sending message to current topic:", topic); // Add logging - if (message.startsWith('~')) { + if (message.startsWith('>')) { await handleCommand(message, { eventEmitter, currentTopic: topic, // Pass the current topic as a string diff --git a/commands.js b/commands.js index 912c9cc..2131078 100644 --- a/commands.js +++ b/commands.js @@ -23,33 +23,33 @@ export default async function handleCommand(command, context) { console.log("Current topic:", currentTopic); // Add logging to check the current topic switch (cmd) { - case '~clear': + case '>clear': clearMessages(currentTopic); break; - case '~ping': + case '>ping': addMessage('LinkUp', 'pong', agentAvatar, currentTopic); break; - case '~help': - addMessage('LinkUp', 'Available commands:\n- ~clear\n- ~ping\n- ~help\n- ~join [topic]\n- ~leave\n- ~create [alias]\n- ~list-files', agentAvatar, currentTopic); + case '>help': + addMessage('LinkUp', 'Available commands:\n- >clear\n- >ping\n- >help\n- >join [topic]\n- >leave\n- >create [alias]\n- >list-files', agentAvatar, currentTopic); break; - case '~join': + case '>join': if (restArgs) { await joinRoom(currentTopic, restArgs); } else { - addMessage('LinkUp', 'Usage: ~join [topic]', agentAvatar, currentTopic); + addMessage('LinkUp', 'Usage: >join [topic]', agentAvatar, currentTopic); } break; - case '~leave': + case '>leave': leaveRoom(currentTopic); break; - case '~create': + case '>create': if (restArgs) { await createRoom(currentTopic, restArgs); } else { - addMessage('LinkUp', 'Usage: ~create [alias]', agentAvatar, currentTopic); + addMessage('LinkUp', 'Usage: >create [alias]', agentAvatar, currentTopic); } break; - case '~list-files': + case '>list-files': const files = await listFiles(); const fileList = files.length > 0 ? files.map(file => `- ${file}`).join('\n') : 'No files available'; addMessage('LinkUp', `Available files:\n${fileList}`, agentAvatar, currentTopic); From 70bc69ddeaa8b39c7f8563e2301a3ea7d19f4703 Mon Sep 17 00:00:00 2001 From: Raven Scott Date: Sun, 7 Jul 2024 22:45:55 -0400 Subject: [PATCH 19/20] auto play audio messages --- app.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app.js b/app.js index cd2ec92..bc3cb81 100644 --- a/app.js +++ b/app.js @@ -1076,6 +1076,7 @@ function addAudioMessage(name, audioUrl, avatar, topic) { const audioElement = document.createElement('audio'); audioElement.src = audioUrl; audioElement.controls = true; + audioElement.autoplay = true; audioElement.classList.add('message-audio'); messageContent.appendChild(senderName); @@ -1089,6 +1090,7 @@ function addAudioMessage(name, audioUrl, avatar, topic) { } } + function addMessage(name, message, avatar, topic) { const container = document.querySelector('#messages'); if (!container) { From 7eddd45f790396c0254ad1c3b9f0daa912a553fc Mon Sep 17 00:00:00 2001 From: Raven Scott Date: Sun, 7 Jul 2024 22:49:15 -0400 Subject: [PATCH 20/20] Only auto play on peers --- app.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app.js b/app.js index bc3cb81..df2a563 100644 --- a/app.js +++ b/app.js @@ -1047,7 +1047,6 @@ function addFileMessage(name, fileName, fileUrl, fileType, avatar, topic) { container.scrollTop = container.scrollHeight; } } - function addAudioMessage(name, audioUrl, avatar, topic) { const container = document.querySelector('#messages'); if (!container) { @@ -1076,7 +1075,10 @@ function addAudioMessage(name, audioUrl, avatar, topic) { const audioElement = document.createElement('audio'); audioElement.src = audioUrl; audioElement.controls = true; - audioElement.autoplay = true; + // Autoplay only if the message is from a peer + if (name !== config.userName) { + audioElement.autoplay = true; + } audioElement.classList.add('message-audio'); messageContent.appendChild(senderName);