From b9db313effca7bc7e638df2edf083da920185b3d Mon Sep 17 00:00:00 2001 From: Raven Scott Date: Tue, 11 Jun 2024 17:40:02 -0400 Subject: [PATCH 01/31] Fix chatBot --- chatBot/includes/Client.js | 6 +++++- chatBot/includes/FileMessage.js | 6 +++--- chatBot/includes/TextMessage.js | 8 ++++---- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/chatBot/includes/Client.js b/chatBot/includes/Client.js index 6c76306..45f3ec7 100644 --- a/chatBot/includes/Client.js +++ b/chatBot/includes/Client.js @@ -7,7 +7,7 @@ import FileMessage from "./FileMessage.js"; class Client extends EventEmitter { constructor(botName) { super(); - if(!botName) return console.error("Bot Name is not defined!"); + if (!botName) return console.error("Bot Name is not defined!"); this.botName = botName; this.swarm = new Hyperswarm(); this.joinedRooms = new Set(); // Track the rooms the bot has joined @@ -48,6 +48,10 @@ class Client extends EventEmitter { } joinChatRoom(chatRoomID) { + if (!chatRoomID || typeof chatRoomID !== 'string') { + return console.error("Invalid chat room ID!"); + } + this.joinedRooms.add(chatRoomID); // Add the room to the list of joined rooms this.currentTopic = chatRoomID; // Store the current topic this.discovery = this.swarm.join(Buffer.from(chatRoomID, 'hex'), { client: true, server: true }); diff --git a/chatBot/includes/FileMessage.js b/chatBot/includes/FileMessage.js index b4a78f4..aa055fe 100644 --- a/chatBot/includes/FileMessage.js +++ b/chatBot/includes/FileMessage.js @@ -1,5 +1,5 @@ class FileMessage { - public FileMessage(peerName, fileName, fileUrl, fileType, peerAvatar, topic, timestamp) { + constructor(peerName, fileName, fileUrl, fileType, peerAvatar, topic, timestamp) { this.peerName = peerName; this.fileName = fileName; this.fileUrl = fileUrl; @@ -9,7 +9,7 @@ class FileMessage { this.timestamp = timestamp; } - public toJsonString() { + toJsonString() { return JSON.stringify({ type: 'file', name: this.peerName, @@ -23,4 +23,4 @@ class FileMessage { } } -export default FileMessage; \ No newline at end of file +export default FileMessage; diff --git a/chatBot/includes/TextMessage.js b/chatBot/includes/TextMessage.js index b691113..fa07783 100644 --- a/chatBot/includes/TextMessage.js +++ b/chatBot/includes/TextMessage.js @@ -1,5 +1,5 @@ class TextMessage { - public TextMessage(peerName, peerAvatar, topic, message, timestamp) { + constructor(peerName, peerAvatar, topic, message, timestamp) { this.peerName = peerName; this.peerAvatar = peerAvatar; this.topic = topic; @@ -7,7 +7,7 @@ class TextMessage { this.timestamp = timestamp; } - public toJsonString() { + toJsonString() { return JSON.stringify({ type: 'message', name: this.peerName, @@ -18,9 +18,9 @@ class TextMessage { }); } - public static new(bot, message) { + static new(bot, message) { return new TextMessage(bot.botName, "", bot.currentTopic, message, Date.now()); } } -export default TextMessage; \ No newline at end of file +export default TextMessage; From 052fbecc20bec254fc47417285ce2873b3f619a6 Mon Sep 17 00:00:00 2001 From: Raven Scott Date: Wed, 12 Jun 2024 00:08:34 -0400 Subject: [PATCH 02/31] Remove not needed join line --- chatBot/bot.js | 1 - 1 file changed, 1 deletion(-) diff --git a/chatBot/bot.js b/chatBot/bot.js index f6ed08e..fc952a6 100644 --- a/chatBot/bot.js +++ b/chatBot/bot.js @@ -64,7 +64,6 @@ loadCommands().then(commands => { }); bot.joinChatRoom(process.env.LINKUP_ROOM_ID); - bot.joinChatRoom(process.env.LINKUP_ROOM_ID2); }).catch(error => { console.error('Error loading commands:', error); }); From 7461dadf88dbff47006849ea42f61c13c90894b1 Mon Sep 17 00:00:00 2001 From: Raven Scott Date: Thu, 13 Jun 2024 01:38:10 -0400 Subject: [PATCH 03/31] remove unused function --- .gitignore | 1 + app.js | 21 --------------------- 2 files changed, 1 insertion(+), 21 deletions(-) diff --git a/.gitignore b/.gitignore index e87fbcc..5919b2c 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ chatBot/.env chatBot/commands/ai.js config.json .idea +AIBot \ No newline at end of file diff --git a/app.js b/app.js index 30b0d47..5c31e12 100644 --- a/app.js +++ b/app.js @@ -440,27 +440,6 @@ async function handleFileInput(event) { } } -function sendFileMessage(name, fileUrl, fileType, avatar) { - const fileName = fileUrl.split('/').pop(); - const messageObj = JSON.stringify({ - type: 'file', - name, - fileName, - fileUrl, - fileType, - avatar, - topic: document.querySelector('#chat-room-topic').innerText, - timestamp: Date.now(), - }); - - const peers = [...swarm.connections]; - for (const peer of peers) { - peer.write(messageObj); - } - - addFileMessage(name, fileName, fileUrl, fileType, avatar, document.querySelector('#chat-room-topic').innerText); -} - 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'); From 060308ec922ea28c591d9619518e80e495b1b51b Mon Sep 17 00:00:00 2001 From: Raven Scott Date: Thu, 13 Jun 2024 01:44:44 -0400 Subject: [PATCH 04/31] Remove localStorage uses --- app.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/app.js b/app.js index 5c31e12..2fe4caa 100644 --- a/app.js +++ b/app.js @@ -14,7 +14,6 @@ const drive = new Hyperdrive(store); await drive.ready(); let swarm; -let registeredUsers = JSON.parse(localStorage.getItem('registeredUsers')) || {}; let peerCount = 0; let activeRooms = []; const eventEmitter = new EventEmitter(); @@ -26,7 +25,8 @@ let servePort; let config = { userName: '', userAvatar: '', - rooms: [] + rooms: [], + registeredUsers: {} }; // Store messages for each room @@ -97,8 +97,8 @@ async function initialize() { config.rooms.forEach(room => { room.alias = room.alias || truncateHash(room.topic); }); - for (let user in registeredUsers) { - registeredUsers[user] = updatePortInUrl(registeredUsers[user]); + for (let user in config.registeredUsers) { + config.registeredUsers[user] = updatePortInUrl(config.registeredUsers[user]); } renderRoomList(); // Render the room list with aliases @@ -189,7 +189,7 @@ function registerUser(e) { e.preventDefault(); const regUsername = document.querySelector('#reg-username').value; - if (registeredUsers[regUsername]) { + if (config.registeredUsers[regUsername]) { alert('Username already taken. Please choose another.'); return; } @@ -201,8 +201,8 @@ function registerUser(e) { const buffer = new Uint8Array(event.target.result); await drive.put(`/icons/${regUsername}.png`, buffer); config.userAvatar = `http://localhost:${servePort}/icons/${regUsername}.png`; // Set the correct URL - registeredUsers[regUsername] = `http://localhost:${servePort}/icons/${regUsername}.png`; // Use placeholder URL - localStorage.setItem('registeredUsers', JSON.stringify(registeredUsers)); + config.registeredUsers[regUsername] = `http://localhost:${servePort}/icons/${regUsername}.png`; // Use placeholder URL + writeConfigToFile("./config.json"); continueRegistration(regUsername); }; reader.readAsArrayBuffer(avatarFile); From 6b3f043332b34922f87b715b26fd4f0a3426fa56 Mon Sep 17 00:00:00 2001 From: Raven Scott Date: Thu, 13 Jun 2024 03:53:23 -0400 Subject: [PATCH 05/31] first attempt at audio messages --- app.js | 110 +++++++++++++++++++++++++++++++++++++++++++++++++++++ index.html | 1 + style.css | 16 ++++++++ 3 files changed, 127 insertions(+) diff --git a/app.js b/app.js index 2fe4caa..565b1f7 100644 --- a/app.js +++ b/app.js @@ -54,6 +54,7 @@ async function initialize() { const removeRoomBtn = document.querySelector('#remove-room-btn'); const attachFileButton = document.getElementById('attach-file'); const fileInput = document.getElementById('file-input'); + const talkButton = document.getElementById('talk-btn'); if (registerForm) { registerForm.addEventListener('submit', registerUser); @@ -87,6 +88,9 @@ async function initialize() { if (fileInput) { fileInput.addEventListener('change', handleFileInput); } + if (talkButton) { + setupTalkButton(); + } const configExists = fs.existsSync("./config.json"); if (configExists) { @@ -138,6 +142,12 @@ async function initialize() { } } else if (messageObj.type === 'message') { onMessageAdded(messageObj.name, messageObj.message, messageObj.avatar, messageObj.topic); + } else if (messageObj.type === 'audio') { + const audioBuffer = b4a.from(messageObj.audio, 'base64'); + const filePath = `/audio/${Date.now()}.webm`; + await drive.put(filePath, audioBuffer); + const audioUrl = `http://localhost:${servePort}${filePath}`; + addAudioMessage(messageObj.name, audioUrl, messageObj.avatar, messageObj.topic); } else { console.error('Received unknown message type:', messageObj); } @@ -185,6 +195,68 @@ async function initialize() { }); } +function setupTalkButton() { + const talkButton = document.getElementById('talk-btn'); + if (!talkButton) return; + + let mediaRecorder; + let audioChunks = []; + + talkButton.addEventListener('mousedown', async () => { + const stream = await navigator.mediaDevices.getUserMedia({ audio: true }); + mediaRecorder = new MediaRecorder(stream); + mediaRecorder.start(); + + mediaRecorder.addEventListener('dataavailable', event => { + audioChunks.push(event.data); + }); + + mediaRecorder.addEventListener('stop', async () => { + const audioBlob = new Blob(audioChunks); + audioChunks = []; + + const arrayBuffer = await audioBlob.arrayBuffer(); + const buffer = new Uint8Array(arrayBuffer); + + const topic = document.querySelector('#chat-room-topic').innerText; + const filePath = `/audio/${Date.now()}.webm`; + await drive.put(filePath, buffer); + + const audioUrl = `http://localhost:${servePort}${filePath}`; + + const audioMessage = { + type: 'audio', + name: config.userName, + audio: b4a.toString(buffer, 'base64'), + audioType: audioBlob.type, + avatar: updatePortInUrl(config.userAvatar), + topic: topic + }; + + console.log('Sending audio message:', audioMessage); // Debugging log + + const peers = [...swarm.connections]; + for (const peer of peers) { + peer.write(JSON.stringify(audioMessage)); + } + + addAudioMessage(config.userName, audioUrl, config.userAvatar, topic); + }); + }); + + talkButton.addEventListener('mouseup', () => { + if (mediaRecorder) { + mediaRecorder.stop(); + } + }); + + talkButton.addEventListener('mouseleave', () => { + if (mediaRecorder && mediaRecorder.state === 'recording') { + mediaRecorder.stop(); + } + }); +} + function registerUser(e) { e.preventDefault(); const regUsername = document.querySelector('#reg-username').value; @@ -557,6 +629,44 @@ function onMessageAdded(from, message, avatar, topic) { } } +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'); + $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 = audioUrl; + $audio.classList.add('attached-audio'); + $content.appendChild($audio); + + $div.appendChild($content); + + // Only render the message if it's for the current room + const currentTopic = document.querySelector('#chat-room-topic').innerText; + 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)}`; } diff --git a/index.html b/index.html index 0f31c1b..2a8d905 100644 --- a/index.html +++ b/index.html @@ -62,6 +62,7 @@ + diff --git a/style.css b/style.css index 6567450..22f2c79 100644 --- a/style.css +++ b/style.css @@ -268,6 +268,22 @@ textarea::placeholder { margin-left: 0.5rem; /* Add margin between buttons */ } +#talk-btn { + padding: 0.5rem 1rem; /* Add padding to the button */ + margin-left: 0.5rem; /* Add margin between buttons */ + font-size: 14px; + border-radius: 5px; + cursor: pointer; +} + +#talk-btn:active { + color : #fff; /* White text when clicked */ + background-color: #f04747; /* Red color when clicked */ +} + +#talk-btn:hover { +} + /* Main container styles */ main { display: flex; From 145b5b728b0d884cabb316802677fb330cb756bc Mon Sep 17 00:00:00 2001 From: Raven Scott Date: Thu, 13 Jun 2024 05:24:44 -0400 Subject: [PATCH 06/31] Better style for the side bar + removing alert on copy --- index.html | 2 +- style.css | 86 +++++++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 83 insertions(+), 5 deletions(-) diff --git a/index.html b/index.html index 2a8d905..d918404 100644 --- a/index.html +++ b/index.html @@ -91,7 +91,7 @@ if (chatRoomTopic) { const topic = chatRoomTopic.innerText; navigator.clipboard.writeText(topic).then(() => { - alert('Topic copied to clipboard!'); + console.log('Topic copied to clipboard:', topic); }).catch(err => { console.error('Failed to copy topic:', err); }); diff --git a/style.css b/style.css index 22f2c79..d7ac757 100644 --- a/style.css +++ b/style.css @@ -23,15 +23,15 @@ body { border-radius: 3px; text-decoration: none; cursor: pointer; - } +} - .mini-button:hover { +.mini-button:hover { background-color: #0056b3; - } +} .bold { font-weight: bold; - } +} pear-ctrl[data-platform="darwin"] { float: right; margin-top: 4px; } @@ -167,10 +167,12 @@ main { color: white; font-size: 14px; border-radius: 5px; + transition: background-color 0.3s ease, transform 0.3s ease; } #sidebar button:hover { background-color: #5b6eae; + transform: scale(1.05); } #remove-room-btn { @@ -469,3 +471,79 @@ main { /* Removed circular border */ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); } + +/* Updated Room List Styles */ +#room-list { + list-style: none; + padding: 0; + margin: 0; +} + +#room-list li { + padding: 10px; + margin-bottom: 10px; + background-color: #3a3f44; + border-radius: 5px; + cursor: pointer; + display: flex; + justify-content: space-between; + align-items: center; + transition: background-color 0.3s ease; +} + +#room-list li:hover { + background-color: #4a5258; +} + +#room-list li span { + flex: 1; +} + +#room-list li .edit-icon, +#room-list li .delete-icon { + margin-left: 10px; + color: #b0d944; + cursor: pointer; + transition: color 0.3s ease; +} + +#room-list li .edit-icon:hover, +#room-list li .delete-icon:hover { + color: #9ac831; +} + +/* Style for Edit Mode Input Box */ +#room-list li input[type="text"] { + background-color: #2e2e2e; + border: 1px solid #b0d944; + border-radius: 5px; + color: #b0d944; + padding: 5px; + width: calc(100% - 40px); /* Adjusted width to fit inside the list item */ + margin-right: 10px; /* Added margin to separate from icons */ + transition: background-color 0.3s ease, color 0.3s ease, border-color 0.3s ease; +} + +#room-list li input[type="text"]:focus { + outline: none; + background-color: #3a3a3a; +} + +/* Sidebar Button Style */ +#sidebar button { + width: 100%; + padding: 10px; + margin-bottom: 10px; + cursor: pointer; + background-color: #7289da; + border: none; + color: white; + font-size: 14px; + border-radius: 5px; + transition: background-color 0.3s ease, transform 0.3s ease; +} + +#sidebar button:hover { + background-color: #5b6eae; + transform: scale(1.05); +} From 76b00ce5b2ce8113e1ce81975b9fab0b5a308a59 Mon Sep 17 00:00:00 2001 From: Raven Scott Date: Thu, 13 Jun 2024 05:55:14 -0400 Subject: [PATCH 07/31] New dark mode theme, removing green, making UI uniform --- style.css | 141 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 73 insertions(+), 68 deletions(-) diff --git a/style.css b/style.css index d7ac757..fceb187 100644 --- a/style.css +++ b/style.css @@ -17,23 +17,28 @@ body { padding: 3px 7px; font-size: 14px; font-weight: bold; - color: #fff; - background-color: #7289da; + color: #ffffff; + background-color: #464343; border: none; border-radius: 3px; text-decoration: none; cursor: pointer; + transition: background-color 0.3s ease, transform 0.3s ease; } .mini-button:hover { - background-color: #0056b3; + background-color: #181717; + transform: scale(1.05); } .bold { font-weight: bold; } -pear-ctrl[data-platform="darwin"] { float: right; margin-top: 4px; } +pear-ctrl[data-platform="darwin"] { + float: right; + margin-top: 4px; +} pear-ctrl { margin-top: 9px; @@ -50,7 +55,7 @@ pear-ctrl:after { left: 0; top: 0; width: 100%; - background-color: #B0D94413; + background-color: #1f1f1f; filter: drop-shadow(2px 10px 6px #888); } @@ -58,7 +63,6 @@ main { display: flex; flex: 1; overflow: hidden; - /* Ensure no overflow in main */ } #sidebar { @@ -107,27 +111,21 @@ main { #messages-container { flex: 1; overflow-y: auto; - /* Allow vertical scrolling */ overflow-x: hidden; - /* Hide horizontal scrolling */ width: 100%; } #messages-container::-webkit-scrollbar { width: 8px; - /* Set the width of the scrollbar */ } #messages-container::-webkit-scrollbar-thumb { - background-color: #b0d944; - /* Set the color of the scrollbar thumb */ + background-color: #464343; border-radius: 10px; - /* Round the corners of the scrollbar thumb */ } #messages-container::-webkit-scrollbar-track { background: #2e2e2e; - /* Set the color of the scrollbar track */ } #message-form { @@ -142,19 +140,15 @@ main { margin-right: 0.5rem; padding-right: 0.5rem; height: 1.5rem; - /* Initial single line height */ overflow-y: hidden; - /* Hide scrollbar */ } #message:focus { height: auto; - /* Allow the textarea to grow dynamically when focused */ } #message:empty { height: 1.5rem; - /* Ensure single line height when empty */ } #sidebar button { @@ -162,7 +156,7 @@ main { padding: 10px; margin-bottom: 10px; cursor: pointer; - background-color: #7289da; + background-color: #3e3c3c; border: none; color: white; font-size: 14px; @@ -171,7 +165,7 @@ main { } #sidebar button:hover { - background-color: #5b6eae; + background-color: #191919; transform: scale(1.05); } @@ -183,10 +177,12 @@ main { color: white; cursor: pointer; border-radius: 5px; + transition: background-color 0.3s ease, transform 0.3s ease; } #remove-room-btn:hover { background-color: #c03535; + transform: scale(1.05); } .hidden { @@ -223,67 +219,71 @@ header { padding: 0.5rem; border-radius: 4px; cursor: pointer; - transition: background-color 0.3s ease; + transition: background-color 0.3s ease, transform 0.3s ease; } .window-controls button:hover { background-color: #3e3e3e; + transform: scale(1.05); } /* Button and input styles */ button, input, textarea { - border: 1px solid #b0d944; + border: 1px solid #464343; background-color: #333; - color: #b0d944; + color: #e0e0e0; padding: 0.5rem; - /* Reduced padding */ font-family: 'Roboto Mono', monospace; font-size: 1rem; line-height: 1.25rem; - /* Adjusted line height */ border-radius: 4px; - transition: background-color 0.3s ease, color 0.3s ease, border-color 0.3s ease; + transition: background-color 0.3s ease, color 0.3s ease, border-color 0.3s ease, transform 0.3s ease; resize: none; - /* Prevent resizing */ overflow-y: hidden; - /* Hide scrollbar */ +} + +button:hover, +input[type="submit"]:hover { + background-color: #191919; + transform: scale(1.05); } textarea { height: auto; - /* Allow the textarea to grow dynamically */ } textarea:focus { outline: none; - /* Remove focus outline */ } textarea::placeholder { color: #a0a0a0; } -#attach-file, #message-form input[type="submit"] { - padding: 0.5rem 1rem; /* Add padding to buttons */ - margin-left: 0.5rem; /* Add margin between buttons */ +#attach-file, +#message-form input[type="submit"] { + padding: 0.5rem 1rem; + margin-left: 0.5rem; } #talk-btn { - padding: 0.5rem 1rem; /* Add padding to the button */ - margin-left: 0.5rem; /* Add margin between buttons */ + padding: 0.5rem 1rem; + margin-left: 0.5rem; font-size: 14px; border-radius: 5px; cursor: pointer; } #talk-btn:active { - color : #fff; /* White text when clicked */ - background-color: #f04747; /* Red color when clicked */ + color: #fff; + background-color: #f04747; } #talk-btn:hover { + background-color: #191919; + transform: scale(1.05); } /* Main container styles */ @@ -315,7 +315,7 @@ main { #or { margin: 1.5rem 0; - color: #b0d944; + color: #e0e0e0; } #loading { @@ -359,6 +359,12 @@ main { #join-chat-room-container button { margin-left: 0.5rem; + transition: background-color 0.3s ease, transform 0.3s ease; +} + +#join-chat-room-container button:hover { + background-color: #191919; + transform: scale(1.05); } #details { @@ -370,17 +376,21 @@ main { border-radius: 4px; width: 100%; box-sizing: border-box; - /* Added to ensure box model includes padding */ } #details>div { display: flex; flex-direction: column; - /* Allow peers count to stack */ } #submit-button { margin-left: 1rem; + transition: background-color 0.3s ease, transform 0.3s ease; +} + +#submit-button:hover { + background-color: #191919; + transform: scale(1.05); } #messages { @@ -388,7 +398,6 @@ main { min-height: 200px; overflow-y: auto; padding: 0.5rem; - /* Reduced padding */ background-color: #262626; border-radius: 4px; display: flex; @@ -431,7 +440,6 @@ main { display: flex; align-items: flex-start; margin-bottom: 0.5rem; - /* Reduced margin */ } .message img.avatar { @@ -439,24 +447,21 @@ main { height: 32px; border-radius: 50%; margin-right: 0.5rem; - border: 2px solid #b0d944; + border: 2px solid #464343; } .message-content { max-width: 70%; background-color: #2e2e2e; padding: 0.5rem; - /* Reduced padding */ border-radius: 8px; - /* Reduced border radius */ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); } .message-header { font-weight: bold; - color: #b0d944; + color: #e0e0e0; font-size: 0.9rem; - /* Reduced font size */ } .message-text { @@ -468,7 +473,6 @@ main { height: auto; margin-top: 0.5rem; border-radius: 4px; - /* Removed circular border */ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); } @@ -502,25 +506,25 @@ main { #room-list li .edit-icon, #room-list li .delete-icon { margin-left: 10px; - color: #b0d944; + color: #e0e0e0; cursor: pointer; transition: color 0.3s ease; } #room-list li .edit-icon:hover, #room-list li .delete-icon:hover { - color: #9ac831; + color: #a0a0a0; } /* Style for Edit Mode Input Box */ #room-list li input[type="text"] { background-color: #2e2e2e; - border: 1px solid #b0d944; + border: 1px solid #464343; border-radius: 5px; - color: #b0d944; + color: #e0e0e0; padding: 5px; - width: calc(100% - 40px); /* Adjusted width to fit inside the list item */ - margin-right: 10px; /* Added margin to separate from icons */ + width: calc(100% - 40px); + margin-right: 10px; transition: background-color 0.3s ease, color 0.3s ease, border-color 0.3s ease; } @@ -529,21 +533,22 @@ main { background-color: #3a3a3a; } -/* Sidebar Button Style */ -#sidebar button { - width: 100%; - padding: 10px; - margin-bottom: 10px; - cursor: pointer; - background-color: #7289da; - border: none; - color: white; - font-size: 14px; - border-radius: 5px; - transition: background-color 0.3s ease, transform 0.3s ease; +/* 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; } -#sidebar button:hover { - background-color: #5b6eae; - transform: scale(1.05); +a:hover { + color: #b0b0b0; /* Lighter color on hover */ + text-decoration: underline; /* Underline on hover */ } + +a:active { + color: #a0a0a0; /* Slightly darker color on active */ +} + +a:visited { + color: #b0b0b0; /* Different color for visited links */ +} \ No newline at end of file From 6a7eb78d3462d55796dfc14e693b4e95ef2aacce Mon Sep 17 00:00:00 2001 From: Raven Scott Date: Thu, 13 Jun 2024 06:10:26 -0400 Subject: [PATCH 08/31] fixing talk button not turning red --- style.css | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/style.css b/style.css index fceb187..6d0f986 100644 --- a/style.css +++ b/style.css @@ -281,10 +281,6 @@ textarea::placeholder { background-color: #f04747; } -#talk-btn:hover { - background-color: #191919; - transform: scale(1.05); -} /* Main container styles */ main { @@ -551,4 +547,4 @@ a:active { a:visited { color: #b0b0b0; /* Different color for visited links */ -} \ No newline at end of file +} From 3c803c811e23f2411b3b1969d142cae0e552625e Mon Sep 17 00:00:00 2001 From: Raven Scott Date: Thu, 13 Jun 2024 14:50:23 -0400 Subject: [PATCH 09/31] Debug --- app.js | 301 +++++++++++++++++++++++++++++---------------------------- 1 file changed, 155 insertions(+), 146 deletions(-) diff --git a/app.js b/app.js index 565b1f7..f771de0 100644 --- a/app.js +++ b/app.js @@ -38,161 +38,165 @@ function getRandomPort() { } async function initialize() { - swarm = new Hyperswarm(); + try { + swarm = new Hyperswarm(); - servePort = getRandomPort(); - const serve = new ServeDrive({ port: servePort, get: ({ key, filename, version }) => drive }); - await serve.ready(); - console.log('Listening on http://localhost:' + serve.address().port); + servePort = getRandomPort(); + const serve = new ServeDrive({ port: servePort, get: ({ key, filename, version }) => drive }); + await serve.ready(); + console.log('Listening on http://localhost:' + serve.address().port); - const registerForm = document.querySelector('#register-form'); - const selectAvatarButton = document.querySelector('#select-avatar'); - const createChatRoomButton = document.querySelector('#create-chat-room'); - const joinChatRoomButton = document.querySelector('#join-chat-room'); - const messageForm = document.querySelector('#message-form'); - const toggleSetupBtn = document.querySelector('#toggle-setup-btn'); - const removeRoomBtn = document.querySelector('#remove-room-btn'); - const attachFileButton = document.getElementById('attach-file'); - const fileInput = document.getElementById('file-input'); - const talkButton = document.getElementById('talk-btn'); + const registerForm = document.querySelector('#register-form'); + const selectAvatarButton = document.querySelector('#select-avatar'); + const createChatRoomButton = document.querySelector('#create-chat-room'); + const joinChatRoomButton = document.querySelector('#join-chat-room'); + const messageForm = document.querySelector('#message-form'); + const toggleSetupBtn = document.querySelector('#toggle-setup-btn'); + const removeRoomBtn = document.querySelector('#remove-room-btn'); + const attachFileButton = document.getElementById('attach-file'); + const fileInput = document.getElementById('file-input'); + const talkButton = document.getElementById('talk-btn'); - if (registerForm) { - registerForm.addEventListener('submit', registerUser); - } - if (selectAvatarButton) { - selectAvatarButton.addEventListener('click', () => { - document.querySelector('#avatar-file').click(); - }); - } - if (createChatRoomButton) { - createChatRoomButton.addEventListener('click', createChatRoom); - } - if (joinChatRoomButton) { - joinChatRoomButton.addEventListener('click', joinChatRoom); - } - if (messageForm) { - messageForm.addEventListener('submit', sendMessage); - } - if (toggleSetupBtn) { - toggleSetupBtn.addEventListener('click', toggleSetupView); - } - if (removeRoomBtn) { - removeRoomBtn.addEventListener('click', () => { - const topic = document.querySelector('#chat-room-topic').innerText; - leaveRoom(topic); - }); - } - if (attachFileButton) { - attachFileButton.addEventListener('click', () => fileInput.click()); - } - if (fileInput) { - fileInput.addEventListener('change', handleFileInput); - } - if (talkButton) { - setupTalkButton(); - } - - const configExists = fs.existsSync("./config.json"); - if (configExists) { - 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]); + if (registerForm) { + registerForm.addEventListener('submit', registerUser); } - - renderRoomList(); // Render the room list with aliases - - // Connect to all rooms on startup - for (const room of config.rooms) { - const topicBuffer = b4a.from(room.topic, 'hex'); - await joinSwarm(topicBuffer); - } - } - - const registerDiv = document.querySelector('#register'); - if (registerDiv && !configExists) { - registerDiv.classList.remove('hidden'); - } - - eventEmitter.on('onMessage', async (messageObj) => { - console.log('Received message:', messageObj); // Debugging log - - if (messageObj.type === 'icon') { - const username = messageObj.username; - if (messageObj.avatar) { - const avatarBuffer = b4a.from(messageObj.avatar, 'base64'); - await drive.put(`/icons/${username}.png`, avatarBuffer); - updateIcon(username, avatarBuffer); - } else { - console.error('Received icon message with missing avatar data:', messageObj); - } - } else if (messageObj.type === 'file') { - if (messageObj.file && messageObj.fileName) { - const fileBuffer = b4a.from(messageObj.file, 'base64'); - await drive.put(`/files/${messageObj.fileName}`, fileBuffer); - const fileUrl = `http://localhost:${servePort}/files/${messageObj.fileName}`; - addFileMessage(messageObj.name, messageObj.fileName, updatePortInUrl(fileUrl), messageObj.fileType, updatePortInUrl(messageObj.avatar), messageObj.topic); - } else { - console.error('Received file message with missing file data or fileName:', messageObj); - } - } else if (messageObj.type === 'message') { - onMessageAdded(messageObj.name, messageObj.message, messageObj.avatar, messageObj.topic); - } else if (messageObj.type === 'audio') { - const audioBuffer = b4a.from(messageObj.audio, 'base64'); - const filePath = `/audio/${Date.now()}.webm`; - await drive.put(filePath, audioBuffer); - const audioUrl = `http://localhost:${servePort}${filePath}`; - addAudioMessage(messageObj.name, audioUrl, messageObj.avatar, messageObj.topic); - } else { - console.error('Received unknown message type:', messageObj); - } - }); - - swarm.on('connection', async (connection, info) => { - peerCount++; - updatePeerCount(); - console.log('Peer connected, current peer count:', peerCount); - - // Send the current user's icon to the new peer - const iconBuffer = await drive.get(`/icons/${config.userName}.png`); - if (iconBuffer) { - const iconMessage = JSON.stringify({ - type: 'icon', - username: config.userName, - avatar: b4a.toString(iconBuffer, 'base64'), + if (selectAvatarButton) { + selectAvatarButton.addEventListener('click', () => { + document.querySelector('#avatar-file').click(); }); - connection.write(iconMessage); + } + if (createChatRoomButton) { + createChatRoomButton.addEventListener('click', createChatRoom); + } + if (joinChatRoomButton) { + joinChatRoomButton.addEventListener('click', joinChatRoom); + } + if (messageForm) { + messageForm.addEventListener('submit', sendMessage); + } + if (toggleSetupBtn) { + toggleSetupBtn.addEventListener('click', toggleSetupView); + } + if (removeRoomBtn) { + removeRoomBtn.addEventListener('click', () => { + const topic = document.querySelector('#chat-room-topic').innerText; + leaveRoom(topic); + }); + } + if (attachFileButton) { + attachFileButton.addEventListener('click', () => fileInput.click()); + } + if (fileInput) { + fileInput.addEventListener('change', handleFileInput); + } + if (talkButton) { + setupTalkButton(); } - connection.on('data', (data) => { - const messageObj = JSON.parse(data.toString()); - eventEmitter.emit('onMessage', messageObj); + const configExists = fs.existsSync("./config.json"); + if (configExists) { + 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]); + } + + renderRoomList(); // Render the room list with aliases + + // Connect to all rooms on startup + for (const room of config.rooms) { + const topicBuffer = b4a.from(room.topic, 'hex'); + await joinSwarm(topicBuffer); + } + } + + const registerDiv = document.querySelector('#register'); + if (registerDiv && !configExists) { + registerDiv.classList.remove('hidden'); + } + + eventEmitter.on('onMessage', async (messageObj) => { + console.log('Received message:', messageObj); // Debugging log + + if (messageObj.type === 'icon') { + const username = messageObj.username; + if (messageObj.avatar) { + const avatarBuffer = b4a.from(messageObj.avatar, 'base64'); + await drive.put(`/icons/${username}.png`, avatarBuffer); + updateIcon(username, avatarBuffer); + } else { + console.error('Received icon message with missing avatar data:', messageObj); + } + } else if (messageObj.type === 'file') { + if (messageObj.file && messageObj.fileName) { + const fileBuffer = b4a.from(messageObj.file, 'base64'); + await drive.put(`/files/${messageObj.fileName}`, fileBuffer); + const fileUrl = `http://localhost:${servePort}/files/${messageObj.fileName}`; + addFileMessage(messageObj.name, messageObj.fileName, updatePortInUrl(fileUrl), messageObj.fileType, updatePortInUrl(messageObj.avatar), messageObj.topic); + } else { + console.error('Received file message with missing file data or fileName:', messageObj); + } + } else if (messageObj.type === 'message') { + onMessageAdded(messageObj.name, messageObj.message, messageObj.avatar, messageObj.topic); + } else if (messageObj.type === 'audio') { + const audioBuffer = b4a.from(messageObj.audio, 'base64'); + const filePath = `/audio/${Date.now()}.webm`; + await drive.put(filePath, audioBuffer); + const audioUrl = `http://localhost:${servePort}${filePath}`; + addAudioMessage(messageObj.name, audioUrl, messageObj.avatar, messageObj.topic); + } else { + console.error('Received unknown message type:', messageObj); + } }); - connection.on('close', () => { - peerCount--; + swarm.on('connection', async (connection, info) => { + peerCount++; updatePeerCount(); - console.log('Peer disconnected, current peer count:', peerCount); + console.log('Peer connected, current peer count:', peerCount); + + // Send the current user's icon to the new peer + const iconBuffer = await drive.get(`/icons/${config.userName}.png`); + if (iconBuffer) { + const iconMessage = JSON.stringify({ + type: 'icon', + username: config.userName, + avatar: b4a.toString(iconBuffer, 'base64'), + }); + connection.write(iconMessage); + } + + connection.on('data', (data) => { + const messageObj = JSON.parse(data.toString()); + eventEmitter.emit('onMessage', messageObj); + }); + + connection.on('close', () => { + peerCount--; + updatePeerCount(); + console.log('Peer disconnected, current peer count:', peerCount); + }); }); - }); - swarm.on('error', (err) => { - console.error('Swarm error:', err); - }); + swarm.on('error', (err) => { + console.error('Swarm error:', err); + }); - swarm.on('close', () => { - console.log('Swarm closed'); - }); + swarm.on('close', () => { + console.log('Swarm closed'); + }); - // Initialize highlight.js once the DOM is fully loaded - document.addEventListener("DOMContentLoaded", (event) => { - hljs.highlightAll(); - }); + // Initialize highlight.js once the DOM is fully loaded + document.addEventListener("DOMContentLoaded", (event) => { + hljs.highlightAll(); + }); + } catch (error) { + console.error('Error during initialization:', error); + } } function setupTalkButton() { @@ -321,14 +325,18 @@ async function joinChatRoom(e) { async function joinSwarm(topicBuffer) { const topic = b4a.toString(topicBuffer, 'hex'); if (!activeRooms.some(room => room.topic === topic)) { - const discovery = swarm.join(topicBuffer, { client: true, server: true }); - await discovery.flushed(); + try { + const discovery = swarm.join(topicBuffer, { client: true, server: true }); + await discovery.flushed(); - activeRooms.push({ topic, discovery }); + activeRooms.push({ topic, discovery }); - console.log('Joined room:', topic); // Debugging log + console.log('Joined room:', topic); // Debugging log - renderMessagesForRoom(topic); + renderMessagesForRoom(topic); + } catch (error) { + console.error('Error joining swarm for topic:', topic, error); + } } } @@ -667,6 +675,7 @@ function addAudioMessage(from, audioUrl, avatar, topic) { 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)}`; } From 8c4a38ca8e18e27e51e4a2d39003e8daed8e3b32 Mon Sep 17 00:00:00 2001 From: Raven Scott Date: Thu, 13 Jun 2024 16:00:02 -0400 Subject: [PATCH 10/31] peer count per room --- app.js | 107 +++++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 81 insertions(+), 26 deletions(-) diff --git a/app.js b/app.js index f771de0..f180a67 100644 --- a/app.js +++ b/app.js @@ -14,7 +14,6 @@ const drive = new Hyperdrive(store); await drive.ready(); let swarm; -let peerCount = 0; let activeRooms = []; const eventEmitter = new EventEmitter(); @@ -32,6 +31,12 @@ let config = { // Store messages for each room let messagesStore = {}; +// Store peer counts for each room +let peerCounts = {}; + +// Map to store discovery instances and their associated topics +const discoveryTopicsMap = new Map(); + // Function to get a random port between 49152 and 65535 function getRandomPort() { return Math.floor(Math.random() * (65535 - 49152 + 1)) + 49152; @@ -155,31 +160,47 @@ async function initialize() { }); swarm.on('connection', async (connection, info) => { - peerCount++; - updatePeerCount(); - console.log('Peer connected, current peer count:', peerCount); + try { + const discovery = [...discoveryTopicsMap.entries()].find(([key, value]) => key.id === info.id); + const topic = discovery ? discovery[1] : null; - // Send the current user's icon to the new peer - const iconBuffer = await drive.get(`/icons/${config.userName}.png`); - if (iconBuffer) { - const iconMessage = JSON.stringify({ - type: 'icon', - username: config.userName, - avatar: b4a.toString(iconBuffer, 'base64'), + if (!topic) { + console.error('No topic found in connection info:', info); + return; + } + + if (!peerCounts[topic]) { + peerCounts[topic] = 0; + } + peerCounts[topic]++; + updatePeerCount(topic); + console.log('Peer connected, current peer count:', peerCounts[topic]); + + // Send the current user's icon to the new peer + const iconBuffer = await drive.get(`/icons/${config.userName}.png`); + if (iconBuffer) { + const iconMessage = JSON.stringify({ + type: 'icon', + username: config.userName, + avatar: b4a.toString(iconBuffer, 'base64'), + topic + }); + connection.write(iconMessage); + } + + connection.on('data', (data) => { + const messageObj = JSON.parse(data.toString()); + eventEmitter.emit('onMessage', messageObj); }); - connection.write(iconMessage); + + connection.on('close', () => { + peerCounts[topic]--; + updatePeerCount(topic); + console.log('Peer disconnected, current peer count:', peerCounts[topic]); + }); + } catch (error) { + console.error('Error handling connection:', error); } - - connection.on('data', (data) => { - const messageObj = JSON.parse(data.toString()); - eventEmitter.emit('onMessage', messageObj); - }); - - connection.on('close', () => { - peerCount--; - updatePeerCount(); - console.log('Peer disconnected, current peer count:', peerCount); - }); }); swarm.on('error', (err) => { @@ -330,6 +351,10 @@ async function joinSwarm(topicBuffer) { await discovery.flushed(); activeRooms.push({ topic, discovery }); + peerCounts[topic] = 0; // Initialize peer count for the new room + + // Store the topic in the map + discoveryTopicsMap.set(discovery, topic); console.log('Joined room:', topic); // Debugging log @@ -343,9 +368,13 @@ async function joinSwarm(topicBuffer) { function addRoomToList(topic) { const roomList = document.querySelector('#room-list'); const roomItem = document.createElement('li'); - roomItem.textContent = truncateHash(topic); roomItem.dataset.topic = topic; + const roomName = document.createElement('span'); + roomName.textContent = truncateHash(topic); + + roomItem.appendChild(roomName); + roomItem.addEventListener('dblclick', () => enterEditMode(roomItem)); roomItem.addEventListener('click', () => switchRoom(topic)); roomList.appendChild(roomItem); @@ -433,6 +462,9 @@ function switchRoom(topic) { // Show chat UI elements document.querySelector('#chat').classList.remove('hidden'); document.querySelector('#setup').classList.add('hidden'); + + // Update the peer count in the header + updateHeaderPeerCount(topic); } function leaveRoom(topic) { @@ -441,6 +473,7 @@ function leaveRoom(topic) { const room = activeRooms[roomIndex]; room.discovery.destroy(); activeRooms.splice(roomIndex, 1); + discoveryTopicsMap.delete(room.discovery); } const roomItem = document.querySelector(`li[data-topic="${topic}"]`); @@ -568,10 +601,32 @@ function addFileMessage(from, fileName, fileUrl, fileType, avatar, topic) { } } -function updatePeerCount() { +function updatePeerCount(topic) { + let peerCountElement = document.querySelector(`#peers-count-${topic}`); + if (!peerCountElement) { + const roomItem = document.querySelector(`#room-list li[data-topic="${topic}"]`); + if (roomItem) { + peerCountElement = document.createElement('span'); + peerCountElement.id = `peers-count-${topic}`; + // roomItem.appendChild(peerCountElement); + } + } + if (peerCountElement) { + peerCountElement.textContent = ` (${peerCounts[topic]})`; // Display the peer count for the specific topic + } + + // Update the header peer count if the current room matches the topic + const currentTopic = document.querySelector('#chat-room-topic').innerText; + if (currentTopic === topic) { + updateHeaderPeerCount(topic); + } +} + +function updateHeaderPeerCount(topic) { const peerCountElement = document.querySelector('#peers-count'); if (peerCountElement) { - peerCountElement.textContent = peerCount; // Display the actual peer count + const peerCount = peerCounts[topic] || 0; + peerCountElement.textContent = peerCount; } } From 5edcff29e1785c055d401d968eff0e011ffbb56c Mon Sep 17 00:00:00 2001 From: snxraven Date: Thu, 13 Jun 2024 20:06:49 +0000 Subject: [PATCH 11/31] revert 8c4a38ca8e18e27e51e4a2d39003e8daed8e3b32 revert peer count per room --- app.js | 107 ++++++++++++++------------------------------------------- 1 file changed, 26 insertions(+), 81 deletions(-) diff --git a/app.js b/app.js index f180a67..f771de0 100644 --- a/app.js +++ b/app.js @@ -14,6 +14,7 @@ const drive = new Hyperdrive(store); await drive.ready(); let swarm; +let peerCount = 0; let activeRooms = []; const eventEmitter = new EventEmitter(); @@ -31,12 +32,6 @@ let config = { // Store messages for each room let messagesStore = {}; -// Store peer counts for each room -let peerCounts = {}; - -// Map to store discovery instances and their associated topics -const discoveryTopicsMap = new Map(); - // Function to get a random port between 49152 and 65535 function getRandomPort() { return Math.floor(Math.random() * (65535 - 49152 + 1)) + 49152; @@ -160,47 +155,31 @@ async function initialize() { }); swarm.on('connection', async (connection, info) => { - try { - const discovery = [...discoveryTopicsMap.entries()].find(([key, value]) => key.id === info.id); - const topic = discovery ? discovery[1] : null; + peerCount++; + updatePeerCount(); + console.log('Peer connected, current peer count:', peerCount); - if (!topic) { - console.error('No topic found in connection info:', info); - return; - } - - if (!peerCounts[topic]) { - peerCounts[topic] = 0; - } - peerCounts[topic]++; - updatePeerCount(topic); - console.log('Peer connected, current peer count:', peerCounts[topic]); - - // Send the current user's icon to the new peer - const iconBuffer = await drive.get(`/icons/${config.userName}.png`); - if (iconBuffer) { - const iconMessage = JSON.stringify({ - type: 'icon', - username: config.userName, - avatar: b4a.toString(iconBuffer, 'base64'), - topic - }); - connection.write(iconMessage); - } - - connection.on('data', (data) => { - const messageObj = JSON.parse(data.toString()); - eventEmitter.emit('onMessage', messageObj); + // Send the current user's icon to the new peer + const iconBuffer = await drive.get(`/icons/${config.userName}.png`); + if (iconBuffer) { + const iconMessage = JSON.stringify({ + type: 'icon', + username: config.userName, + avatar: b4a.toString(iconBuffer, 'base64'), }); - - connection.on('close', () => { - peerCounts[topic]--; - updatePeerCount(topic); - console.log('Peer disconnected, current peer count:', peerCounts[topic]); - }); - } catch (error) { - console.error('Error handling connection:', error); + connection.write(iconMessage); } + + connection.on('data', (data) => { + const messageObj = JSON.parse(data.toString()); + eventEmitter.emit('onMessage', messageObj); + }); + + connection.on('close', () => { + peerCount--; + updatePeerCount(); + console.log('Peer disconnected, current peer count:', peerCount); + }); }); swarm.on('error', (err) => { @@ -351,10 +330,6 @@ async function joinSwarm(topicBuffer) { await discovery.flushed(); activeRooms.push({ topic, discovery }); - peerCounts[topic] = 0; // Initialize peer count for the new room - - // Store the topic in the map - discoveryTopicsMap.set(discovery, topic); console.log('Joined room:', topic); // Debugging log @@ -368,13 +343,9 @@ async function joinSwarm(topicBuffer) { function addRoomToList(topic) { const roomList = document.querySelector('#room-list'); const roomItem = document.createElement('li'); + roomItem.textContent = truncateHash(topic); roomItem.dataset.topic = topic; - const roomName = document.createElement('span'); - roomName.textContent = truncateHash(topic); - - roomItem.appendChild(roomName); - roomItem.addEventListener('dblclick', () => enterEditMode(roomItem)); roomItem.addEventListener('click', () => switchRoom(topic)); roomList.appendChild(roomItem); @@ -462,9 +433,6 @@ function switchRoom(topic) { // Show chat UI elements document.querySelector('#chat').classList.remove('hidden'); document.querySelector('#setup').classList.add('hidden'); - - // Update the peer count in the header - updateHeaderPeerCount(topic); } function leaveRoom(topic) { @@ -473,7 +441,6 @@ function leaveRoom(topic) { const room = activeRooms[roomIndex]; room.discovery.destroy(); activeRooms.splice(roomIndex, 1); - discoveryTopicsMap.delete(room.discovery); } const roomItem = document.querySelector(`li[data-topic="${topic}"]`); @@ -601,32 +568,10 @@ function addFileMessage(from, fileName, fileUrl, fileType, avatar, topic) { } } -function updatePeerCount(topic) { - let peerCountElement = document.querySelector(`#peers-count-${topic}`); - if (!peerCountElement) { - const roomItem = document.querySelector(`#room-list li[data-topic="${topic}"]`); - if (roomItem) { - peerCountElement = document.createElement('span'); - peerCountElement.id = `peers-count-${topic}`; - // roomItem.appendChild(peerCountElement); - } - } - if (peerCountElement) { - peerCountElement.textContent = ` (${peerCounts[topic]})`; // Display the peer count for the specific topic - } - - // Update the header peer count if the current room matches the topic - const currentTopic = document.querySelector('#chat-room-topic').innerText; - if (currentTopic === topic) { - updateHeaderPeerCount(topic); - } -} - -function updateHeaderPeerCount(topic) { +function updatePeerCount() { const peerCountElement = document.querySelector('#peers-count'); if (peerCountElement) { - const peerCount = peerCounts[topic] || 0; - peerCountElement.textContent = peerCount; + peerCountElement.textContent = peerCount; // Display the actual peer count } } From 0a3732fb076dfaa5ea469597d53df460836fbc88 Mon Sep 17 00:00:00 2001 From: Raven Scott Date: Thu, 13 Jun 2024 19:23:42 -0400 Subject: [PATCH 12/31] Re adding per room topic counts --- app.js | 109 +++++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 82 insertions(+), 27 deletions(-) diff --git a/app.js b/app.js index f771de0..7d33ed3 100644 --- a/app.js +++ b/app.js @@ -14,7 +14,6 @@ const drive = new Hyperdrive(store); await drive.ready(); let swarm; -let peerCount = 0; let activeRooms = []; const eventEmitter = new EventEmitter(); @@ -32,6 +31,12 @@ let config = { // Store messages for each room let messagesStore = {}; +// Store peer counts for each room +let peerCounts = {}; + +// Map to store discovery instances and their associated topics +const discoveryTopicsMap = new Map(); + // Function to get a random port between 49152 and 65535 function getRandomPort() { return Math.floor(Math.random() * (65535 - 49152 + 1)) + 49152; @@ -155,31 +160,47 @@ async function initialize() { }); swarm.on('connection', async (connection, info) => { - peerCount++; - updatePeerCount(); - console.log('Peer connected, current peer count:', peerCount); + try { + const discovery = [...discoveryTopicsMap.entries()].find(([key, value]) => key.id === info.id); + const topic = discovery ? discovery[1] : null; - // Send the current user's icon to the new peer - const iconBuffer = await drive.get(`/icons/${config.userName}.png`); - if (iconBuffer) { - const iconMessage = JSON.stringify({ - type: 'icon', - username: config.userName, - avatar: b4a.toString(iconBuffer, 'base64'), + if (!topic) { + console.error('No topic found in connection info:', info); + return; + } + + if (!peerCounts[topic]) { + peerCounts[topic] = 0; + } + peerCounts[topic]++; + updatePeerCount(topic); + console.log('Peer connected, current peer count:', peerCounts[topic]); + + // Send the current user's icon to the new peer + const iconBuffer = await drive.get(`/icons/${config.userName}.png`); + if (iconBuffer) { + const iconMessage = JSON.stringify({ + type: 'icon', + username: config.userName, + avatar: b4a.toString(iconBuffer, 'base64'), + topic + }); + connection.write(iconMessage); + } + + connection.on('data', (data) => { + const messageObj = JSON.parse(data.toString()); + eventEmitter.emit('onMessage', messageObj); }); - connection.write(iconMessage); + + connection.on('close', () => { + peerCounts[topic]--; + updatePeerCount(topic); + console.log('Peer disconnected, current peer count:', peerCounts[topic]); + }); + } catch (error) { + console.error('Error handling connection:', error); } - - connection.on('data', (data) => { - const messageObj = JSON.parse(data.toString()); - eventEmitter.emit('onMessage', messageObj); - }); - - connection.on('close', () => { - peerCount--; - updatePeerCount(); - console.log('Peer disconnected, current peer count:', peerCount); - }); }); swarm.on('error', (err) => { @@ -330,6 +351,10 @@ async function joinSwarm(topicBuffer) { await discovery.flushed(); activeRooms.push({ topic, discovery }); + peerCounts[topic] = 0; // Initialize peer count for the new room + + // Store the topic in the map + discoveryTopicsMap.set(discovery, topic); console.log('Joined room:', topic); // Debugging log @@ -343,9 +368,13 @@ async function joinSwarm(topicBuffer) { function addRoomToList(topic) { const roomList = document.querySelector('#room-list'); const roomItem = document.createElement('li'); - roomItem.textContent = truncateHash(topic); roomItem.dataset.topic = topic; + const roomName = document.createElement('span'); + roomName.textContent = truncateHash(topic); + + roomItem.appendChild(roomName); + roomItem.addEventListener('dblclick', () => enterEditMode(roomItem)); roomItem.addEventListener('click', () => switchRoom(topic)); roomList.appendChild(roomItem); @@ -433,6 +462,9 @@ function switchRoom(topic) { // Show chat UI elements document.querySelector('#chat').classList.remove('hidden'); document.querySelector('#setup').classList.add('hidden'); + + // Update the peer count in the header + updateHeaderPeerCount(topic); } function leaveRoom(topic) { @@ -441,6 +473,7 @@ function leaveRoom(topic) { const room = activeRooms[roomIndex]; room.discovery.destroy(); activeRooms.splice(roomIndex, 1); + discoveryTopicsMap.delete(room.discovery); } const roomItem = document.querySelector(`li[data-topic="${topic}"]`); @@ -568,10 +601,32 @@ function addFileMessage(from, fileName, fileUrl, fileType, avatar, topic) { } } -function updatePeerCount() { +function updatePeerCount(topic) { + let peerCountElement = document.querySelector(`#peers-count-${topic}`); + if (!peerCountElement) { + const roomItem = document.querySelector(`#room-list li[data-topic="${topic}"]`); + if (roomItem) { + peerCountElement = document.createElement('span'); + peerCountElement.id = `peers-count-${topic}`; + // roomItem.appendChild(peerCountElement); + } + } + if (peerCountElement) { + peerCountElement.textContent = ` (${peerCounts[topic]})`; // Display the peer count for the specific topic + } + + // Update the header peer count if the current room matches the topic + const currentTopic = document.querySelector('#chat-room-topic').innerText; + if (currentTopic === topic) { + updateHeaderPeerCount(topic); + } +} + +function updateHeaderPeerCount(topic) { const peerCountElement = document.querySelector('#peers-count'); if (peerCountElement) { - peerCountElement.textContent = peerCount; // Display the actual peer count + const peerCount = peerCounts[topic] || 0; + peerCountElement.textContent = peerCount; } } @@ -759,4 +814,4 @@ function addMessageToStore(topic, messageObj) { // Call this function when loading the rooms initially renderRoomList(); -initialize(); +initialize(); \ No newline at end of file From 6b1b13f75eef6b903fb250b2f60f0a754a4eaece Mon Sep 17 00:00:00 2001 From: snxraven Date: Thu, 13 Jun 2024 23:41:30 +0000 Subject: [PATCH 13/31] revert 0a3732fb076dfaa5ea469597d53df460836fbc88 revert Re adding per room topic counts --- app.js | 109 ++++++++++++++------------------------------------------- 1 file changed, 27 insertions(+), 82 deletions(-) diff --git a/app.js b/app.js index 7d33ed3..f771de0 100644 --- a/app.js +++ b/app.js @@ -14,6 +14,7 @@ const drive = new Hyperdrive(store); await drive.ready(); let swarm; +let peerCount = 0; let activeRooms = []; const eventEmitter = new EventEmitter(); @@ -31,12 +32,6 @@ let config = { // Store messages for each room let messagesStore = {}; -// Store peer counts for each room -let peerCounts = {}; - -// Map to store discovery instances and their associated topics -const discoveryTopicsMap = new Map(); - // Function to get a random port between 49152 and 65535 function getRandomPort() { return Math.floor(Math.random() * (65535 - 49152 + 1)) + 49152; @@ -160,47 +155,31 @@ async function initialize() { }); swarm.on('connection', async (connection, info) => { - try { - const discovery = [...discoveryTopicsMap.entries()].find(([key, value]) => key.id === info.id); - const topic = discovery ? discovery[1] : null; + peerCount++; + updatePeerCount(); + console.log('Peer connected, current peer count:', peerCount); - if (!topic) { - console.error('No topic found in connection info:', info); - return; - } - - if (!peerCounts[topic]) { - peerCounts[topic] = 0; - } - peerCounts[topic]++; - updatePeerCount(topic); - console.log('Peer connected, current peer count:', peerCounts[topic]); - - // Send the current user's icon to the new peer - const iconBuffer = await drive.get(`/icons/${config.userName}.png`); - if (iconBuffer) { - const iconMessage = JSON.stringify({ - type: 'icon', - username: config.userName, - avatar: b4a.toString(iconBuffer, 'base64'), - topic - }); - connection.write(iconMessage); - } - - connection.on('data', (data) => { - const messageObj = JSON.parse(data.toString()); - eventEmitter.emit('onMessage', messageObj); + // Send the current user's icon to the new peer + const iconBuffer = await drive.get(`/icons/${config.userName}.png`); + if (iconBuffer) { + const iconMessage = JSON.stringify({ + type: 'icon', + username: config.userName, + avatar: b4a.toString(iconBuffer, 'base64'), }); - - connection.on('close', () => { - peerCounts[topic]--; - updatePeerCount(topic); - console.log('Peer disconnected, current peer count:', peerCounts[topic]); - }); - } catch (error) { - console.error('Error handling connection:', error); + connection.write(iconMessage); } + + connection.on('data', (data) => { + const messageObj = JSON.parse(data.toString()); + eventEmitter.emit('onMessage', messageObj); + }); + + connection.on('close', () => { + peerCount--; + updatePeerCount(); + console.log('Peer disconnected, current peer count:', peerCount); + }); }); swarm.on('error', (err) => { @@ -351,10 +330,6 @@ async function joinSwarm(topicBuffer) { await discovery.flushed(); activeRooms.push({ topic, discovery }); - peerCounts[topic] = 0; // Initialize peer count for the new room - - // Store the topic in the map - discoveryTopicsMap.set(discovery, topic); console.log('Joined room:', topic); // Debugging log @@ -368,13 +343,9 @@ async function joinSwarm(topicBuffer) { function addRoomToList(topic) { const roomList = document.querySelector('#room-list'); const roomItem = document.createElement('li'); + roomItem.textContent = truncateHash(topic); roomItem.dataset.topic = topic; - const roomName = document.createElement('span'); - roomName.textContent = truncateHash(topic); - - roomItem.appendChild(roomName); - roomItem.addEventListener('dblclick', () => enterEditMode(roomItem)); roomItem.addEventListener('click', () => switchRoom(topic)); roomList.appendChild(roomItem); @@ -462,9 +433,6 @@ function switchRoom(topic) { // Show chat UI elements document.querySelector('#chat').classList.remove('hidden'); document.querySelector('#setup').classList.add('hidden'); - - // Update the peer count in the header - updateHeaderPeerCount(topic); } function leaveRoom(topic) { @@ -473,7 +441,6 @@ function leaveRoom(topic) { const room = activeRooms[roomIndex]; room.discovery.destroy(); activeRooms.splice(roomIndex, 1); - discoveryTopicsMap.delete(room.discovery); } const roomItem = document.querySelector(`li[data-topic="${topic}"]`); @@ -601,32 +568,10 @@ function addFileMessage(from, fileName, fileUrl, fileType, avatar, topic) { } } -function updatePeerCount(topic) { - let peerCountElement = document.querySelector(`#peers-count-${topic}`); - if (!peerCountElement) { - const roomItem = document.querySelector(`#room-list li[data-topic="${topic}"]`); - if (roomItem) { - peerCountElement = document.createElement('span'); - peerCountElement.id = `peers-count-${topic}`; - // roomItem.appendChild(peerCountElement); - } - } - if (peerCountElement) { - peerCountElement.textContent = ` (${peerCounts[topic]})`; // Display the peer count for the specific topic - } - - // Update the header peer count if the current room matches the topic - const currentTopic = document.querySelector('#chat-room-topic').innerText; - if (currentTopic === topic) { - updateHeaderPeerCount(topic); - } -} - -function updateHeaderPeerCount(topic) { +function updatePeerCount() { const peerCountElement = document.querySelector('#peers-count'); if (peerCountElement) { - const peerCount = peerCounts[topic] || 0; - peerCountElement.textContent = peerCount; + peerCountElement.textContent = peerCount; // Display the actual peer count } } @@ -814,4 +759,4 @@ function addMessageToStore(topic, messageObj) { // Call this function when loading the rooms initially renderRoomList(); -initialize(); \ No newline at end of file +initialize(); From b50bd0bbabfba6f8bd8dc8b9625ef9bd0ed9e78e Mon Sep 17 00:00:00 2001 From: Raven Scott Date: Thu, 13 Jun 2024 20:02:01 -0400 Subject: [PATCH 14/31] Room Counts done better --- app.js | 116 ++++++++++++++++++++++++------------------------- chatBot/bot.js | 29 +++++++------ 2 files changed, 74 insertions(+), 71 deletions(-) diff --git a/app.js b/app.js index 7d33ed3..40b0888 100644 --- a/app.js +++ b/app.js @@ -130,6 +130,7 @@ async function initialize() { if (messageObj.type === 'icon') { const username = messageObj.username; + const topic = messageObj.topic; if (messageObj.avatar) { const avatarBuffer = b4a.from(messageObj.avatar, 'base64'); await drive.put(`/icons/${username}.png`, avatarBuffer); @@ -137,6 +138,7 @@ async function initialize() { } else { console.error('Received icon message with missing avatar data:', messageObj); } + // updatePeerCount(topic); } else if (messageObj.type === 'file') { if (messageObj.file && messageObj.fileName) { const fileBuffer = b4a.from(messageObj.file, 'base64'); @@ -161,20 +163,38 @@ async function initialize() { swarm.on('connection', async (connection, info) => { try { - const discovery = [...discoveryTopicsMap.entries()].find(([key, value]) => key.id === info.id); - const topic = discovery ? discovery[1] : null; + connection.on('data', (data) => { + const messageObj = JSON.parse(data.toString()); + if (messageObj.type === 'icon' && messageObj.topic) { + connection.topic = messageObj.topic; + if (!peerCounts[messageObj.topic]) { + peerCounts[messageObj.topic] = 0; + } + peerCounts[messageObj.topic]++; + updateHeaderPeerCount(messageObj.topic); + } + eventEmitter.emit('onMessage', messageObj); + }); - if (!topic) { - console.error('No topic found in connection info:', info); - return; - } + connection.on('close', () => { + const topic = connection.topic; + if (topic && peerCounts[topic]) { + peerCounts[topic]--; + updateHeaderPeerCount(topic); + console.log('Peer disconnected, current peer count:', peerCounts[topic]); + } + }); - if (!peerCounts[topic]) { - peerCounts[topic] = 0; + // Sending current user's topic to the new peer + const currentTopic = document.querySelector('#chat-room-topic').innerText; + connection.topic = currentTopic; + + if (!peerCounts[currentTopic]) { + peerCounts[currentTopic] = 0; } - peerCounts[topic]++; - updatePeerCount(topic); - console.log('Peer connected, current peer count:', peerCounts[topic]); + peerCounts[currentTopic]++; + updateHeaderPeerCount(currentTopic); + console.log('Peer connected, current peer count:', peerCounts[currentTopic]); // Send the current user's icon to the new peer const iconBuffer = await drive.get(`/icons/${config.userName}.png`); @@ -183,21 +203,17 @@ async function initialize() { type: 'icon', username: config.userName, avatar: b4a.toString(iconBuffer, 'base64'), - topic + topic: currentTopic + }); + connection.write(iconMessage); + } else { + const iconMessage = JSON.stringify({ + type: 'icon', + username: config.userName, + topic: currentTopic }); connection.write(iconMessage); } - - connection.on('data', (data) => { - const messageObj = JSON.parse(data.toString()); - eventEmitter.emit('onMessage', messageObj); - }); - - connection.on('close', () => { - peerCounts[topic]--; - updatePeerCount(topic); - console.log('Peer disconnected, current peer count:', peerCounts[topic]); - }); } catch (error) { console.error('Error handling connection:', error); } @@ -601,27 +617,6 @@ function addFileMessage(from, fileName, fileUrl, fileType, avatar, topic) { } } -function updatePeerCount(topic) { - let peerCountElement = document.querySelector(`#peers-count-${topic}`); - if (!peerCountElement) { - const roomItem = document.querySelector(`#room-list li[data-topic="${topic}"]`); - if (roomItem) { - peerCountElement = document.createElement('span'); - peerCountElement.id = `peers-count-${topic}`; - // roomItem.appendChild(peerCountElement); - } - } - if (peerCountElement) { - peerCountElement.textContent = ` (${peerCounts[topic]})`; // Display the peer count for the specific topic - } - - // Update the header peer count if the current room matches the topic - const currentTopic = document.querySelector('#chat-room-topic').innerText; - if (currentTopic === topic) { - updateHeaderPeerCount(topic); - } -} - function updateHeaderPeerCount(topic) { const peerCountElement = document.querySelector('#peers-count'); if (peerCountElement) { @@ -637,12 +632,13 @@ function scrollToBottom() { function onMessageAdded(from, message, avatar, topic) { console.log('Adding message:', { from, message, avatar, topic }); // Debugging log + const messageObj = { from, message, avatar }; - + // Add the message to the store addMessageToStore(topic, messageObj); @@ -667,19 +663,24 @@ function onMessageAdded(from, message, avatar, topic) { const $text = document.createElement('div'); $text.classList.add('message-text'); - const md = window.markdownit({ - highlight: function (str, lang) { - if (lang && hljs.getLanguage(lang)) { - try { - return hljs.highlight(str, { language: lang }).value; - } catch (__) {} + if (typeof message === 'string') { + 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 } - return ''; // use external default escaping - } - }); + }); - const markdownContent = md.render(message); - $text.innerHTML = markdownContent; + const markdownContent = md.render(message); + $text.innerHTML = markdownContent; + } else { + console.error('Message content is not a string:', message); + $text.textContent = 'Invalid message content.'; + } $content.appendChild($header); $content.appendChild($text); @@ -799,7 +800,6 @@ function renderMessagesForRoom(topic) { onMessageAdded(message.from, message.message, message.avatar, topic); }); } - function getMessagesForRoom(topic) { return messagesStore[topic] || []; } @@ -814,4 +814,4 @@ function addMessageToStore(topic, messageObj) { // Call this function when loading the rooms initially renderRoomList(); -initialize(); \ No newline at end of file +initialize(); diff --git a/chatBot/bot.js b/chatBot/bot.js index fc952a6..3e4b961 100644 --- a/chatBot/bot.js +++ b/chatBot/bot.js @@ -5,6 +5,7 @@ import 'dotenv/config'; // Create a new instance of the chatBot class with a valid botName const botName = process.env.BOT_NAME; // Replace 'MyBot' with the desired bot name +const linkupRoomId = process.env.LINKUP_ROOM_ID; // Use the environment variable for room ID // Load commands from the 'commands' directory const commandsDir = path.join(new URL('./commands/', import.meta.url).pathname); @@ -41,29 +42,31 @@ loadCommands().then(commands => { bot.on('onMessage', (peer, message) => { console.log(message); - console.log(`Message received from ${message.name}@${message.topic} at ${new Date(message.timestamp).toLocaleTimeString()}: ${message.message}`); + console.log(`Message received from ${message.peerName}@${message.topic} at ${new Date(message.timestamp).toLocaleTimeString()}: ${message.message}`); - // Check if the message starts with a command prefix - if (message.message.startsWith('!')) { - // Extract the command and arguments - const [command, ...args] = message.message.slice(1).split(' '); + // Handle all messages as potential AI commands + const userMessage = message.message; - // Find the corresponding command handler - const commandHandler = commands[command.toLowerCase()]; + // Find the corresponding command handler (assuming the AI command handler is in 'commands/ai.js') + const commandHandler = commands['ai']; - // If the command exists, execute its handler - if (commandHandler && typeof commandHandler.handler === 'function') { - commandHandler.handler(bot, args, message); - } + // If the command exists, execute its handler + if (commandHandler && typeof commandHandler.handler === 'function') { + commandHandler.handler(bot, [userMessage], message); } }); bot.on('onBotJoinRoom', () => { console.log("Bot is ready!"); - bot.sendTextMessage(process.env.ON_READY_MESSAGE); + // Include topic in the message sent when the bot joins the room + bot.sendTextMessage({ + text: process.env.ON_READY_MESSAGE, + topic: linkupRoomId + }); }); - bot.joinChatRoom(process.env.LINKUP_ROOM_ID); + // Ensure the bot joins the chat room with the topic information + bot.joinChatRoom(linkupRoomId); }).catch(error => { console.error('Error loading commands:', error); }); From 116b23d70f6f568accc39c562eb021d529f13ef5 Mon Sep 17 00:00:00 2001 From: Raven Scott Date: Thu, 13 Jun 2024 20:47:45 -0400 Subject: [PATCH 15/31] Move back to global peer count --- app.js | 2 +- chatBot/bot.js | 31 ++++++++++++++----------------- 2 files changed, 15 insertions(+), 18 deletions(-) diff --git a/app.js b/app.js index f771de0..d90279f 100644 --- a/app.js +++ b/app.js @@ -759,4 +759,4 @@ function addMessageToStore(topic, messageObj) { // Call this function when loading the rooms initially renderRoomList(); -initialize(); +initialize(); \ No newline at end of file diff --git a/chatBot/bot.js b/chatBot/bot.js index 3e4b961..3ef3d0e 100644 --- a/chatBot/bot.js +++ b/chatBot/bot.js @@ -5,7 +5,6 @@ import 'dotenv/config'; // Create a new instance of the chatBot class with a valid botName const botName = process.env.BOT_NAME; // Replace 'MyBot' with the desired bot name -const linkupRoomId = process.env.LINKUP_ROOM_ID; // Use the environment variable for room ID // Load commands from the 'commands' directory const commandsDir = path.join(new URL('./commands/', import.meta.url).pathname); @@ -42,31 +41,29 @@ loadCommands().then(commands => { bot.on('onMessage', (peer, message) => { console.log(message); - console.log(`Message received from ${message.peerName}@${message.topic} at ${new Date(message.timestamp).toLocaleTimeString()}: ${message.message}`); + console.log(`Message received from ${message.name}@${message.topic} at ${new Date(message.timestamp).toLocaleTimeString()}: ${message.message}`); - // Handle all messages as potential AI commands - const userMessage = message.message; + // Check if the message starts with a command prefix + if (message.message.startsWith('!')) { + // Extract the command and arguments + const [command, ...args] = message.message.slice(1).split(' '); - // Find the corresponding command handler (assuming the AI command handler is in 'commands/ai.js') - const commandHandler = commands['ai']; + // Find the corresponding command handler + const commandHandler = commands[command.toLowerCase()]; - // If the command exists, execute its handler - if (commandHandler && typeof commandHandler.handler === 'function') { - commandHandler.handler(bot, [userMessage], message); + // If the command exists, execute its handler + if (commandHandler && typeof commandHandler.handler === 'function') { + commandHandler.handler(bot, args, message); + } } }); bot.on('onBotJoinRoom', () => { console.log("Bot is ready!"); - // Include topic in the message sent when the bot joins the room - bot.sendTextMessage({ - text: process.env.ON_READY_MESSAGE, - topic: linkupRoomId - }); + bot.sendTextMessage(process.env.ON_READY_MESSAGE); }); - // Ensure the bot joins the chat room with the topic information - bot.joinChatRoom(linkupRoomId); + bot.joinChatRoom(process.env.LINKUP_ROOM_ID); }).catch(error => { console.error('Error loading commands:', error); -}); +}); \ No newline at end of file From b1ea06018bffeef05ab4fee938a78f899ace01e4 Mon Sep 17 00:00:00 2001 From: Raven Scott Date: Thu, 13 Jun 2024 21:05:10 -0400 Subject: [PATCH 16/31] Using swarm.connections.size --- app.js | 286 ++++++++++++++++++++++++++++++--------------------------- 1 file changed, 149 insertions(+), 137 deletions(-) diff --git a/app.js b/app.js index d90279f..bf83784 100644 --- a/app.js +++ b/app.js @@ -14,7 +14,6 @@ const drive = new Hyperdrive(store); await drive.ready(); let swarm; -let peerCount = 0; let activeRooms = []; const eventEmitter = new EventEmitter(); @@ -46,151 +45,28 @@ async function initialize() { await serve.ready(); console.log('Listening on http://localhost:' + serve.address().port); - const registerForm = document.querySelector('#register-form'); - const selectAvatarButton = document.querySelector('#select-avatar'); - const createChatRoomButton = document.querySelector('#create-chat-room'); - const joinChatRoomButton = document.querySelector('#join-chat-room'); - const messageForm = document.querySelector('#message-form'); - const toggleSetupBtn = document.querySelector('#toggle-setup-btn'); - const removeRoomBtn = document.querySelector('#remove-room-btn'); - const attachFileButton = document.getElementById('attach-file'); - const fileInput = document.getElementById('file-input'); - const talkButton = document.getElementById('talk-btn'); - - if (registerForm) { - registerForm.addEventListener('submit', registerUser); - } - if (selectAvatarButton) { - selectAvatarButton.addEventListener('click', () => { - document.querySelector('#avatar-file').click(); - }); - } - if (createChatRoomButton) { - createChatRoomButton.addEventListener('click', createChatRoom); - } - if (joinChatRoomButton) { - joinChatRoomButton.addEventListener('click', joinChatRoom); - } - if (messageForm) { - messageForm.addEventListener('submit', sendMessage); - } - if (toggleSetupBtn) { - toggleSetupBtn.addEventListener('click', toggleSetupView); - } - if (removeRoomBtn) { - removeRoomBtn.addEventListener('click', () => { - const topic = document.querySelector('#chat-room-topic').innerText; - leaveRoom(topic); - }); - } - if (attachFileButton) { - attachFileButton.addEventListener('click', () => fileInput.click()); - } - if (fileInput) { - fileInput.addEventListener('change', handleFileInput); - } - if (talkButton) { - setupTalkButton(); - } + // Event listeners setup + setupEventListeners(); const configExists = fs.existsSync("./config.json"); if (configExists) { - 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]); - } - - renderRoomList(); // Render the room list with aliases - - // Connect to all rooms on startup - for (const room of config.rooms) { - const topicBuffer = b4a.from(room.topic, 'hex'); - await joinSwarm(topicBuffer); - } + loadConfigFromFile(); + renderRoomList(); + await connectToAllRooms(); } - const registerDiv = document.querySelector('#register'); - if (registerDiv && !configExists) { - registerDiv.classList.remove('hidden'); + if (!configExists) { + document.querySelector('#register').classList.remove('hidden'); } eventEmitter.on('onMessage', async (messageObj) => { - console.log('Received message:', messageObj); // Debugging log - - if (messageObj.type === 'icon') { - const username = messageObj.username; - if (messageObj.avatar) { - const avatarBuffer = b4a.from(messageObj.avatar, 'base64'); - await drive.put(`/icons/${username}.png`, avatarBuffer); - updateIcon(username, avatarBuffer); - } else { - console.error('Received icon message with missing avatar data:', messageObj); - } - } else if (messageObj.type === 'file') { - if (messageObj.file && messageObj.fileName) { - const fileBuffer = b4a.from(messageObj.file, 'base64'); - await drive.put(`/files/${messageObj.fileName}`, fileBuffer); - const fileUrl = `http://localhost:${servePort}/files/${messageObj.fileName}`; - addFileMessage(messageObj.name, messageObj.fileName, updatePortInUrl(fileUrl), messageObj.fileType, updatePortInUrl(messageObj.avatar), messageObj.topic); - } else { - console.error('Received file message with missing file data or fileName:', messageObj); - } - } else if (messageObj.type === 'message') { - onMessageAdded(messageObj.name, messageObj.message, messageObj.avatar, messageObj.topic); - } else if (messageObj.type === 'audio') { - const audioBuffer = b4a.from(messageObj.audio, 'base64'); - const filePath = `/audio/${Date.now()}.webm`; - await drive.put(filePath, audioBuffer); - const audioUrl = `http://localhost:${servePort}${filePath}`; - addAudioMessage(messageObj.name, audioUrl, messageObj.avatar, messageObj.topic); - } else { - console.error('Received unknown message type:', messageObj); - } + handleIncomingMessage(messageObj); }); - swarm.on('connection', async (connection, info) => { - peerCount++; - updatePeerCount(); - console.log('Peer connected, current peer count:', peerCount); + swarm.on('connection', handleConnection); + swarm.on('error', (err) => console.error('Swarm error:', err)); + swarm.on('close', () => console.log('Swarm closed')); - // Send the current user's icon to the new peer - const iconBuffer = await drive.get(`/icons/${config.userName}.png`); - if (iconBuffer) { - const iconMessage = JSON.stringify({ - type: 'icon', - username: config.userName, - avatar: b4a.toString(iconBuffer, 'base64'), - }); - connection.write(iconMessage); - } - - connection.on('data', (data) => { - const messageObj = JSON.parse(data.toString()); - eventEmitter.emit('onMessage', messageObj); - }); - - connection.on('close', () => { - peerCount--; - updatePeerCount(); - console.log('Peer disconnected, current peer count:', peerCount); - }); - }); - - swarm.on('error', (err) => { - console.error('Swarm error:', err); - }); - - swarm.on('close', () => { - console.log('Swarm closed'); - }); - - // Initialize highlight.js once the DOM is fully loaded document.addEventListener("DOMContentLoaded", (event) => { hljs.highlightAll(); }); @@ -199,6 +75,119 @@ async function initialize() { } } +function setupEventListeners() { + const registerForm = document.querySelector('#register-form'); + const selectAvatarButton = document.querySelector('#select-avatar'); + const createChatRoomButton = document.querySelector('#create-chat-room'); + const joinChatRoomButton = document.querySelector('#join-chat-room'); + const messageForm = document.querySelector('#message-form'); + const toggleSetupBtn = document.querySelector('#toggle-setup-btn'); + const removeRoomBtn = document.querySelector('#remove-room-btn'); + const attachFileButton = document.getElementById('attach-file'); + const fileInput = document.getElementById('file-input'); + const talkButton = document.getElementById('talk-btn'); + + if (registerForm) { + registerForm.addEventListener('submit', registerUser); + } + if (selectAvatarButton) { + selectAvatarButton.addEventListener('click', () => { + document.querySelector('#avatar-file').click(); + }); + } + if (createChatRoomButton) { + createChatRoomButton.addEventListener('click', createChatRoom); + } + if (joinChatRoomButton) { + joinChatRoomButton.addEventListener('click', joinChatRoom); + } + if (messageForm) { + messageForm.addEventListener('submit', sendMessage); + } + if (toggleSetupBtn) { + toggleSetupBtn.addEventListener('click', toggleSetupView); + } + if (removeRoomBtn) { + removeRoomBtn.addEventListener('click', () => { + const topic = document.querySelector('#chat-room-topic').innerText; + leaveRoom(topic); + }); + } + if (attachFileButton) { + attachFileButton.addEventListener('click', () => fileInput.click()); + } + if (fileInput) { + fileInput.addEventListener('change', handleFileInput); + } + if (talkButton) { + setupTalkButton(); + } +} + +function handleIncomingMessage(messageObj) { + console.log('Received message:', messageObj); // Debugging log + + if (messageObj.type === 'icon') { + const username = messageObj.username; + if (messageObj.avatar) { + const avatarBuffer = b4a.from(messageObj.avatar, 'base64'); + drive.put(`/icons/${username}.png`, avatarBuffer).then(() => { + updateIcon(username, avatarBuffer); + }); + } else { + console.error('Received icon message with missing avatar data:', messageObj); + } + } else if (messageObj.type === 'file') { + if (messageObj.file && messageObj.fileName) { + const fileBuffer = b4a.from(messageObj.file, 'base64'); + drive.put(`/files/${messageObj.fileName}`, fileBuffer).then(() => { + const fileUrl = `http://localhost:${servePort}/files/${messageObj.fileName}`; + addFileMessage(messageObj.name, messageObj.fileName, updatePortInUrl(fileUrl), messageObj.fileType, updatePortInUrl(messageObj.avatar), messageObj.topic); + }); + } else { + console.error('Received file message with missing file data or fileName:', messageObj); + } + } else if (messageObj.type === 'message') { + onMessageAdded(messageObj.name, messageObj.message, messageObj.avatar, messageObj.topic); + } else if (messageObj.type === 'audio') { + const audioBuffer = b4a.from(messageObj.audio, 'base64'); + const filePath = `/audio/${Date.now()}.webm`; + drive.put(filePath, audioBuffer).then(() => { + const audioUrl = `http://localhost:${servePort}${filePath}`; + addAudioMessage(messageObj.name, audioUrl, messageObj.avatar, messageObj.topic); + }); + } else { + console.error('Received unknown message type:', messageObj); + } +} + +function handleConnection(connection, info) { + updatePeerCount(); + console.log('Peer connected, current peer count:', swarm.connections.size); + + // Send the current user's icon to the new peer + drive.get(`/icons/${config.userName}.png`).then(iconBuffer => { + if (iconBuffer) { + const iconMessage = JSON.stringify({ + type: 'icon', + username: config.userName, + avatar: b4a.toString(iconBuffer, 'base64'), + }); + connection.write(iconMessage); + } + }); + + connection.on('data', (data) => { + const messageObj = JSON.parse(data.toString()); + eventEmitter.emit('onMessage', messageObj); + }); + + connection.on('close', () => { + updatePeerCount(); + console.log('Peer disconnected, current peer count:', swarm.connections.size); + }); +} + function setupTalkButton() { const talkButton = document.getElementById('talk-btn'); if (!talkButton) return; @@ -334,6 +323,7 @@ async function joinSwarm(topicBuffer) { console.log('Joined room:', topic); // Debugging log renderMessagesForRoom(topic); + updatePeerCount(); } catch (error) { console.error('Error joining swarm for topic:', topic, error); } @@ -429,6 +419,7 @@ function switchRoom(topic) { clearMessages(); renderMessagesForRoom(topic); + updatePeerCount(); // Show chat UI elements document.querySelector('#chat').classList.remove('hidden'); @@ -571,7 +562,7 @@ function addFileMessage(from, fileName, fileUrl, fileType, avatar, topic) { function updatePeerCount() { const peerCountElement = document.querySelector('#peers-count'); if (peerCountElement) { - peerCountElement.textContent = peerCount; // Display the actual peer count + peerCountElement.textContent = swarm.connections.size; // Display the actual peer count } } @@ -756,7 +747,28 @@ function addMessageToStore(topic, messageObj) { messagesStore[topic].push(messageObj); } +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(); \ No newline at end of file +initialize(); From 2faf2b6f78a6b7a99d3c04b3be7aacbb43c8b1b6 Mon Sep 17 00:00:00 2001 From: Raven Scott Date: Thu, 13 Jun 2024 21:44:30 -0400 Subject: [PATCH 17/31] Moving to MultiSwarm with proper connection tracking per topic --- app.js | 92 +++++++++++++++++++++++++++------------------------------- 1 file changed, 42 insertions(+), 50 deletions(-) diff --git a/app.js b/app.js index bf83784..eef5d48 100644 --- a/app.js +++ b/app.js @@ -13,7 +13,6 @@ const drive = new Hyperdrive(store); await drive.ready(); -let swarm; let activeRooms = []; const eventEmitter = new EventEmitter(); @@ -36,10 +35,25 @@ function getRandomPort() { return Math.floor(Math.random() * (65535 - 49152 + 1)) + 49152; } +function currentTopic() { + return document.querySelector('#chat-room-topic').innerText; +} + +function getCurrentPeerCount() { + const topic = currentTopic(); + const room = activeRooms.find(room => room.topic === topic); + return room ? room.swarm.connections.size : 0; +} + +function updatePeerCount() { + const peerCountElement = document.querySelector('#peers-count'); + if (peerCountElement) { + peerCountElement.textContent = getCurrentPeerCount(); // Display the actual peer count + } +} + async function initialize() { try { - swarm = new Hyperswarm(); - servePort = getRandomPort(); const serve = new ServeDrive({ port: servePort, get: ({ key, filename, version }) => drive }); await serve.ready(); @@ -63,10 +77,6 @@ async function initialize() { handleIncomingMessage(messageObj); }); - swarm.on('connection', handleConnection); - swarm.on('error', (err) => console.error('Swarm error:', err)); - swarm.on('close', () => console.log('Swarm closed')); - document.addEventListener("DOMContentLoaded", (event) => { hljs.highlightAll(); }); @@ -109,7 +119,7 @@ function setupEventListeners() { } if (removeRoomBtn) { removeRoomBtn.addEventListener('click', () => { - const topic = document.querySelector('#chat-room-topic').innerText; + const topic = currentTopic(); leaveRoom(topic); }); } @@ -162,30 +172,18 @@ function handleIncomingMessage(messageObj) { } function handleConnection(connection, info) { - updatePeerCount(); - console.log('Peer connected, current peer count:', swarm.connections.size); - - // Send the current user's icon to the new peer - drive.get(`/icons/${config.userName}.png`).then(iconBuffer => { - if (iconBuffer) { - const iconMessage = JSON.stringify({ - type: 'icon', - username: config.userName, - avatar: b4a.toString(iconBuffer, 'base64'), - }); - connection.write(iconMessage); - } - }); - + console.log('New connection', info); connection.on('data', (data) => { const messageObj = JSON.parse(data.toString()); eventEmitter.emit('onMessage', messageObj); }); connection.on('close', () => { + console.log('Connection closed', info); updatePeerCount(); - console.log('Peer disconnected, current peer count:', swarm.connections.size); }); + + updatePeerCount(); } function setupTalkButton() { @@ -211,7 +209,7 @@ function setupTalkButton() { const arrayBuffer = await audioBlob.arrayBuffer(); const buffer = new Uint8Array(arrayBuffer); - const topic = document.querySelector('#chat-room-topic').innerText; + const topic = currentTopic(); const filePath = `/audio/${Date.now()}.webm`; await drive.put(filePath, buffer); @@ -228,7 +226,7 @@ function setupTalkButton() { console.log('Sending audio message:', audioMessage); // Debugging log - const peers = [...swarm.connections]; + const peers = [...activeRooms.find(room => room.topic === topic).swarm.connections]; for (const peer of peers) { peer.write(JSON.stringify(audioMessage)); } @@ -315,10 +313,15 @@ async function joinSwarm(topicBuffer) { const topic = b4a.toString(topicBuffer, 'hex'); if (!activeRooms.some(room => room.topic === topic)) { try { + const swarm = new Hyperswarm(); const discovery = swarm.join(topicBuffer, { client: true, server: true }); await discovery.flushed(); - activeRooms.push({ topic, discovery }); + swarm.on('connection', (connection, info) => { + handleConnection(connection, info); + }); + + activeRooms.push({ topic, swarm, discovery }); console.log('Joined room:', topic); // Debugging log @@ -385,8 +388,7 @@ function exitEditMode(roomItem, input, topic) { } // Check if the edited room is the current room in view - const currentTopic = document.querySelector('#chat-room-topic').innerText; - if (currentTopic === topic) { + if (currentTopic() === topic) { const chatRoomName = document.querySelector('#chat-room-name'); if (chatRoomName) { chatRoomName.innerText = newAlias; @@ -430,7 +432,7 @@ function leaveRoom(topic) { const roomIndex = activeRooms.findIndex(room => room.topic === topic); if (roomIndex !== -1) { const room = activeRooms[roomIndex]; - room.discovery.destroy(); + room.swarm.destroy(); activeRooms.splice(roomIndex, 1); } @@ -455,7 +457,7 @@ function sendMessage(e) { const message = document.querySelector('#message').value; document.querySelector('#message').value = ''; - const topic = document.querySelector('#chat-room-topic').innerText; + const topic = currentTopic(); console.log('Sending message:', message); // Debugging log @@ -470,7 +472,7 @@ function sendMessage(e) { timestamp: Date.now() }); - const peers = [...swarm.connections]; + const peers = [...activeRooms.find(room => room.topic === topic).swarm.connections]; for (const peer of peers) { peer.write(messageObj); } @@ -486,7 +488,7 @@ async function handleFileInput(event) { await drive.put(filePath, buffer); const fileUrl = `http://localhost:${servePort}${filePath}`; - const topic = document.querySelector('#chat-room-topic').innerText; + const topic = currentTopic(); const fileMessage = { type: 'file', @@ -500,7 +502,7 @@ async function handleFileInput(event) { console.log('Sending file message:', fileMessage); // Debugging log - const peers = [...swarm.connections]; + const peers = [...activeRooms.find(room => room.topic === topic).swarm.connections]; for (const peer of peers) { peer.write(JSON.stringify(fileMessage)); } @@ -550,19 +552,11 @@ function addFileMessage(from, fileName, fileUrl, fileType, avatar, topic) { $div.appendChild($content); // Only render the message if it's for the current room - const currentTopic = document.querySelector('#chat-room-topic').innerText; - if (currentTopic === topic) { + 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 updatePeerCount() { - const peerCountElement = document.querySelector('#peers-count'); - if (peerCountElement) { - peerCountElement.textContent = swarm.connections.size; // Display the actual peer count + console.log(`Message for topic ${topic} not rendered because current topic is ${currentTopic()}`); // Debugging log } } @@ -583,8 +577,7 @@ function onMessageAdded(from, message, avatar, topic) { addMessageToStore(topic, messageObj); // Only render messages for the current room - const currentTopic = document.querySelector('#chat-room-topic').innerText; - if (currentTopic === topic) { + if (currentTopic() === topic) { const $div = document.createElement('div'); $div.classList.add('message'); @@ -624,7 +617,7 @@ function onMessageAdded(from, message, avatar, topic) { document.querySelector('#messages').appendChild($div); scrollToBottom(); } else { - console.log(`Message for topic ${topic} not rendered because current topic is ${currentTopic}`); // Debugging log + console.log(`Message for topic ${topic} not rendered because current topic is ${currentTopic()}`); // Debugging log } } @@ -658,12 +651,11 @@ function addAudioMessage(from, audioUrl, avatar, topic) { $div.appendChild($content); // Only render the message if it's for the current room - const currentTopic = document.querySelector('#chat-room-topic').innerText; - if (currentTopic === topic) { + 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 + console.log(`Message for topic ${topic} not rendered because current topic is ${currentTopic()}`); // Debugging log } } From af37909b3fc94f8933f34a9b364aeaee16582c7a Mon Sep 17 00:00:00 2001 From: Raven Scott Date: Thu, 13 Jun 2024 22:29:05 -0400 Subject: [PATCH 18/31] update --- app.js | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/app.js b/app.js index eef5d48..fd4f82b 100644 --- a/app.js +++ b/app.js @@ -173,6 +173,7 @@ function handleIncomingMessage(messageObj) { function handleConnection(connection, info) { console.log('New connection', info); + connection.on('data', (data) => { const messageObj = JSON.parse(data.toString()); eventEmitter.emit('onMessage', messageObj); @@ -183,9 +184,28 @@ function handleConnection(connection, info) { updatePeerCount(); }); + connection.on('error', (error) => { + console.error('Connection error', error); + if (error.code === 'ETIMEDOUT') { + retryConnection(info.topicBuffer); + } + }); + updatePeerCount(); } +function retryConnection(topicBuffer) { + const topic = b4a.toString(topicBuffer, 'hex'); + const room = activeRooms.find(room => room.topic === topic); + if (room) { + console.log('Retrying connection to room:', topic); + room.swarm.leave(topicBuffer); + joinSwarm(topicBuffer).catch((error) => { + console.error('Failed to rejoin room after timeout:', error); + }); + } +} + function setupTalkButton() { const talkButton = document.getElementById('talk-btn'); if (!talkButton) return; @@ -763,4 +783,4 @@ async function connectToAllRooms() { // Call this function when loading the rooms initially renderRoomList(); -initialize(); +initialize(); \ No newline at end of file From 7061aa1e5e20cadc8b1388aa4053a33029da884d Mon Sep 17 00:00:00 2001 From: Raven Scott Date: Thu, 13 Jun 2024 22:41:43 -0400 Subject: [PATCH 19/31] fixes --- app.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app.js b/app.js index fd4f82b..0d3eb91 100644 --- a/app.js +++ b/app.js @@ -539,7 +539,7 @@ function addFileMessage(from, fileName, fileUrl, fileType, avatar, topic) { $div.classList.add('message'); const $img = document.createElement('img'); - $img.src = avatar || 'https://via.placeholder.com/40'; + $img.src = updatePortInUrl(avatar) || 'https://via.placeholder.com/40'; $img.classList.add('avatar'); $div.appendChild($img); @@ -553,7 +553,7 @@ function addFileMessage(from, fileName, fileUrl, fileType, avatar, topic) { if (fileType.startsWith('image/')) { const $image = document.createElement('img'); - $image.src = fileUrl; + $image.src = updatePortInUrl(fileUrl); $image.alt = fileName; $image.classList.add('attached-image'); $content.appendChild($image); @@ -664,7 +664,7 @@ function addAudioMessage(from, audioUrl, avatar, topic) { if (from !== config.userName) { $audio.autoplay = true; // Add autoplay attribute for peers only } - $audio.src = audioUrl; + $audio.src = updatePortInUrl(audioUrl); $audio.classList.add('attached-audio'); $content.appendChild($audio); From 7a31202ac3b2c6a1700ab6f565de9d019b3e2420 Mon Sep 17 00:00:00 2001 From: Raven Scott Date: Thu, 13 Jun 2024 23:12:31 -0400 Subject: [PATCH 20/31] Fixing user avatars/icons concerning all of the major changes today --- app.js | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/app.js b/app.js index 0d3eb91..aa6c3f5 100644 --- a/app.js +++ b/app.js @@ -141,9 +141,8 @@ function handleIncomingMessage(messageObj) { const username = messageObj.username; if (messageObj.avatar) { const avatarBuffer = b4a.from(messageObj.avatar, 'base64'); - drive.put(`/icons/${username}.png`, avatarBuffer).then(() => { - updateIcon(username, avatarBuffer); - }); + drive.put(`/icons/${username}.png`, avatarBuffer); + updateIcon(username, avatarBuffer); } else { console.error('Received icon message with missing avatar data:', messageObj); } @@ -171,9 +170,23 @@ function handleIncomingMessage(messageObj) { } } -function handleConnection(connection, info) { +async function handleConnection(connection, info) { console.log('New connection', info); - + + // Sending the icon immediately upon connection + const iconBuffer = await drive.get(`/icons/${config.userName}.png`); + if (iconBuffer) { + const iconMessage = JSON.stringify({ + type: 'icon', + username: config.userName, + avatar: b4a.toString(iconBuffer, 'base64'), + }); + console.log('Sending icon to new peer:', iconMessage); + connection.write(iconMessage); + } else { + console.error('Icon not found for user:', config.userName); + } + connection.on('data', (data) => { const messageObj = JSON.parse(data.toString()); eventEmitter.emit('onMessage', messageObj); @@ -193,7 +206,6 @@ function handleConnection(connection, info) { updatePeerCount(); } - function retryConnection(topicBuffer) { const topic = b4a.toString(topicBuffer, 'hex'); const room = activeRooms.find(room => room.topic === topic); From 98aa79f07599a45a6263b02959e15b4cf7a9a75e Mon Sep 17 00:00:00 2001 From: Raven Scott Date: Fri, 14 Jun 2024 00:08:32 -0400 Subject: [PATCH 21/31] Prevent Message Duplicates in Message History --- app.js | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/app.js b/app.js index aa6c3f5..e5d0b70 100644 --- a/app.js +++ b/app.js @@ -768,7 +768,19 @@ function addMessageToStore(topic, messageObj) { if (!messagesStore[topic]) { messagesStore[topic] = []; } - messagesStore[topic].push(messageObj); + + // Check for duplicates + 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() { From 7dbcf174295867ee8c0707c57b5f2a4104216117 Mon Sep 17 00:00:00 2001 From: Raven Scott Date: Fri, 14 Jun 2024 00:33:19 -0400 Subject: [PATCH 22/31] Duplicate message fix --- app.js | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/app.js b/app.js index e5d0b70..8b7e586 100644 --- a/app.js +++ b/app.js @@ -141,7 +141,7 @@ function handleIncomingMessage(messageObj) { const username = messageObj.username; if (messageObj.avatar) { const avatarBuffer = b4a.from(messageObj.avatar, 'base64'); - drive.put(`/icons/${username}.png`, avatarBuffer); + drive.put(`/icons/${username}.png`, avatarBuffer); updateIcon(username, avatarBuffer); } else { console.error('Received icon message with missing avatar data:', messageObj); @@ -157,7 +157,7 @@ function handleIncomingMessage(messageObj) { console.error('Received file message with missing file data or fileName:', messageObj); } } else if (messageObj.type === 'message') { - onMessageAdded(messageObj.name, messageObj.message, messageObj.avatar, messageObj.topic); + onMessageAdded(messageObj.name, messageObj.message, messageObj.avatar, messageObj.topic, messageObj.timestamp); } else if (messageObj.type === 'audio') { const audioBuffer = b4a.from(messageObj.audio, 'base64'); const filePath = `/audio/${Date.now()}.webm`; @@ -206,6 +206,7 @@ async function handleConnection(connection, info) { updatePeerCount(); } + function retryConnection(topicBuffer) { const topic = b4a.toString(topicBuffer, 'hex'); const room = activeRooms.find(room => room.topic === topic); @@ -490,10 +491,11 @@ function sendMessage(e) { document.querySelector('#message').value = ''; const topic = currentTopic(); + const timestamp = Date.now(); console.log('Sending message:', message); // Debugging log - onMessageAdded(config.userName, message, config.userAvatar, topic); + onMessageAdded(config.userName, message, config.userAvatar, topic, timestamp); const messageObj = JSON.stringify({ type: 'message', @@ -501,7 +503,7 @@ function sendMessage(e) { message, avatar: config.userAvatar, topic: topic, - timestamp: Date.now() + timestamp: timestamp }); const peers = [...activeRooms.find(room => room.topic === topic).swarm.connections]; @@ -597,12 +599,13 @@ function scrollToBottom() { container.scrollTop = container.scrollHeight; } -function onMessageAdded(from, message, avatar, topic) { +function onMessageAdded(from, message, avatar, topic, timestamp) { console.log('Adding message:', { from, message, avatar, topic }); // Debugging log const messageObj = { from, message, - avatar + avatar, + timestamp: timestamp || Date.now() }; // Add the message to the store @@ -756,7 +759,7 @@ function renderMessagesForRoom(topic) { // Fetch and render messages for the selected room const messages = getMessagesForRoom(topic); messages.forEach(message => { - onMessageAdded(message.from, message.message, message.avatar, topic); + onMessageAdded(message.from, message.message, message.avatar, topic, message.timestamp); }); } @@ -769,10 +772,10 @@ function addMessageToStore(topic, messageObj) { messagesStore[topic] = []; } - // Check for duplicates + // 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.from === messageObj.from && + msg.message === messageObj.message && msg.timestamp === messageObj.timestamp ); @@ -807,4 +810,4 @@ async function connectToAllRooms() { // Call this function when loading the rooms initially renderRoomList(); -initialize(); \ No newline at end of file +initialize(); From b56d603b1eea086148c22d9ac27b72a417d53fa1 Mon Sep 17 00:00:00 2001 From: Raven Scott Date: Fri, 14 Jun 2024 00:57:05 -0400 Subject: [PATCH 23/31] Add internal command handler with ping and clear commands to start --- app.js | 25 ++++++++++++++++++++++++- assets/agent.png | Bin 0 -> 20210 bytes commands.js | 27 +++++++++++++++++++++++++++ 3 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 assets/agent.png create mode 100644 commands.js diff --git a/app.js b/app.js index 8b7e586..fc3dde0 100644 --- a/app.js +++ b/app.js @@ -6,13 +6,22 @@ import Hyperdrive from 'hyperdrive'; import Corestore from 'corestore'; import { EventEmitter } from 'events'; import fs from "fs"; +import handleCommand from './commands.js'; +const agentAvatarPath = './assets/agent.png'; +let agentAvatar = ''; const storagePath = `./storage/`; const store = new Corestore(storagePath); const drive = new Hyperdrive(store); await drive.ready(); +// Load the agent avatar once when the app starts +if (fs.existsSync(agentAvatarPath)) { + const avatarBuffer = fs.readFileSync(agentAvatarPath); + agentAvatar = `data:image/png;base64,${b4a.toString(avatarBuffer, 'base64')}`; +} + let activeRooms = []; const eventEmitter = new EventEmitter(); @@ -484,7 +493,6 @@ function leaveRoom(topic) { document.querySelector('#setup').classList.remove('hidden'); } } - function sendMessage(e) { e.preventDefault(); const message = document.querySelector('#message').value; @@ -493,6 +501,17 @@ function sendMessage(e) { const topic = currentTopic(); const timestamp = Date.now(); + if (message.startsWith('~')) { + // Handle command + handleCommand(message, { + eventEmitter, + currentTopic, + clearMessages, + addMessage: (from, message, avatar, topic) => onMessageAdded(from, message, avatar, topic, timestamp) + }); + return; + } + console.log('Sending message:', message); // Debugging log onMessageAdded(config.userName, message, config.userAvatar, topic, timestamp); @@ -715,6 +734,10 @@ function clearMessages() { while (messagesContainer.firstChild) { messagesContainer.removeChild(messagesContainer.firstChild); } + + // Clear the messages from the store for the current room + const topic = currentTopic(); + messagesStore[topic] = []; } function toggleSetupView() { diff --git a/assets/agent.png b/assets/agent.png new file mode 100644 index 0000000000000000000000000000000000000000..edcb376cf6f31f4c8096ff98e5e956243f82114d GIT binary patch literal 20210 zcmX6^1yoeu*L}m_5Yni0Ns6>I(jZ+wT0n5Yb z;7Smk1_naC4G+k8`Wc7SzcAIfx_4Q{eFofb?>}j`YO1gep570?t+zaxz48#l-v}GW& zvN^r*$IKkE@39pXWbAYv#Obhv;S%L2Ys>bEt&cH^iFgdIpKMCaH>0=b=`&#Id!vD8 zQnsP%hNpTOV)(Wd2QA3vvzPlC z2k~t_jvVsP;EJFXJ&X%fY3z&(ojX$7``LBbuQ^OO5ZUoK+)%Ib-F#gPVm#q;mmiv7 zJ}EuwAE$x>lmJlAcHxkQbl7tI>F~mF%})3unN<}u;g|7)&>0KeLX8)pGlfBg1I7HG z06x%Y%4go8Nx!chZ5+HLPIl)tOK^Y5J5NiG)p>(PbWk!_*5`iWlCqo zt#E)ncpqxw~=N50U%=?e>;$tmInDs zw{NIi9=)nsNQ@J5bz;*c3c!FRhu=uu<1aRmHf9^jw7sL57jl8m*~Zqr1#kc;uvBJt zSt~Cw=JlM1&BDi*x-&c#&wr-0MhngXo_>PK z@s&OPkdI)wr}K?baLsDx_FW?`C4ksqxt$fF6O0hLWMK~Dzx-OoVR_`W#sOC9*SK;c ze`PX&6}|axAcl$uH%i|{0UMk%cK2zT-I~3d3eOUso4jQ`UiW;pG8xjn{dM;yhA@9EC+CkC^Ajrvs%?8L^W}#slb#kflp5adr5P*GWE!j0y0(Sw( zuVxmk7nKCJQl**iLQvW$wgaFhMxN&Qg$_mlbO;_UQqBDMB5MH)mgci;={JHW7r1_2F3jVGa=xab+@qz<_@Bn{lD$qA8rEF#Hb! zh9%^`z>;w+=yV~;h7_FEI0};9x#ml0hQ-!wzG9P=9 zEo;C#@>czxjB>3=C;>3HVbh|BtqK^{gwf!FhBG9`(53lkAeL~K@UXs$1>^9OK!FAI z8|0|Rh$#~zmQVcCuapN8@W(KqDdA?@&Wjv?Fh01yMMA?5EbVn*CGaT zQ906HzP}SdNRnYB(>k%)fUw78?t4ryOMua#>=7b82s|DFh#!VOm;Cd=gqEHsuY@~MiSuuK66!Y2fsg$^A~1VBpi z&Hi{PGq@F?u|}S}RW#rduw%~B2;SoZqPXG4yW#&p`@dX9%0G*c!2*b0z^CXsJP3f0 z>>EtEnCbxlP%u{Q?00w(0D2iWCic~M)WFhXmi*esU6$Zh{B^TZv_cH0(g= z4Z@cgm6~_}0?^CA=d;6ZDS>zl@5jJF(g#4Rd`9jY5?JeYFEWBH8$4I^3UG4@HwdiN zF&z2R1|I?dtzjR>dkHB4UKWhBV;%n{1PD@uFK73XQUW6Em|(j>fobs3hmNk`*r$Ae zi#nQ6Fjd_RK+rtquZD<0p-cIw#Ui@;3kVSMp`%X|MMQw0iN|oT2?8>CREk7xfT(W) zhL(*VkV#B!$Ab_cM&mJNX{YM107O`vksn3lJ?K&hD%f1Y

J!k%t$Z$64|LUYckj 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 literal 0 HcmV?d00001 diff --git a/commands.js b/commands.js new file mode 100644 index 0000000..c9610cd --- /dev/null +++ b/commands.js @@ -0,0 +1,27 @@ +// commands.js +import b4a from 'b4a'; +import fs from 'fs'; + +const agentAvatarPath = './assets/agent.png'; +let agentAvatar = ''; + +// Load the agent avatar once when the module is imported +if (fs.existsSync(agentAvatarPath)) { + const avatarBuffer = fs.readFileSync(agentAvatarPath); + agentAvatar = `data:image/png;base64,${b4a.toString(avatarBuffer, 'base64')}`; +} + +export default function handleCommand(command, context) { + const { eventEmitter, currentTopic, clearMessages, addMessage } = context; + + switch (command.trim().toLowerCase()) { + case '~clear': + clearMessages(); + break; + case '~ping': + addMessage('LinkUp', 'pong', agentAvatar, currentTopic()); + break; + default: + console.log('Unknown command:', command); + } +} \ No newline at end of file From 458954c1e1d9e374795278a69ba826de52ae288e Mon Sep 17 00:00:00 2001 From: Raven Scott Date: Fri, 14 Jun 2024 01:10:23 -0400 Subject: [PATCH 24/31] adding in new commands --- app.js | 16 +++++++++++++--- commands.js | 25 +++++++++++++++++++++---- 2 files changed, 34 insertions(+), 7 deletions(-) diff --git a/app.js b/app.js index fc3dde0..4787082 100644 --- a/app.js +++ b/app.js @@ -61,6 +61,12 @@ function updatePeerCount() { } } +async function joinRoom(topicStr) { + const topicBuffer = b4a.from(topicStr, 'hex'); + addRoomToList(topicStr); + await joinSwarm(topicBuffer); +} + async function initialize() { try { servePort = getRandomPort(); @@ -493,7 +499,9 @@ function leaveRoom(topic) { document.querySelector('#setup').classList.remove('hidden'); } } -function sendMessage(e) { + + +async function sendMessage(e) { e.preventDefault(); const message = document.querySelector('#message').value; document.querySelector('#message').value = ''; @@ -503,11 +511,13 @@ function sendMessage(e) { if (message.startsWith('~')) { // Handle command - handleCommand(message, { + await handleCommand(message, { eventEmitter, currentTopic, clearMessages, - addMessage: (from, message, avatar, topic) => onMessageAdded(from, message, avatar, topic, timestamp) + addMessage: (from, message, avatar, topic) => onMessageAdded(from, message, avatar, topic, timestamp), + joinRoom, + leaveRoom }); return; } diff --git a/commands.js b/commands.js index c9610cd..9d4cf57 100644 --- a/commands.js +++ b/commands.js @@ -11,17 +11,34 @@ if (fs.existsSync(agentAvatarPath)) { agentAvatar = `data:image/png;base64,${b4a.toString(avatarBuffer, 'base64')}`; } -export default function handleCommand(command, context) { - const { eventEmitter, currentTopic, clearMessages, addMessage } = context; +export default async function handleCommand(command, context) { + const { eventEmitter, currentTopic, clearMessages, addMessage, joinRoom, leaveRoom } = context; + + const args = command.trim().split(' '); + const cmd = args[0].toLowerCase(); + const restArgs = args.slice(1).join(' '); - switch (command.trim().toLowerCase()) { + switch (cmd) { case '~clear': clearMessages(); break; case '~ping': addMessage('LinkUp', 'pong', agentAvatar, currentTopic()); break; + case '~help': + addMessage('LinkUp', 'Available commands: ~clear, ~ping, ~help, ~join [topic], ~leave', agentAvatar, currentTopic()); + break; + case '~join': + if (restArgs) { + await joinRoom(restArgs); + } else { + addMessage('LinkUp', 'Usage: ~join [topic]', agentAvatar, currentTopic()); + } + break; + case '~leave': + leaveRoom(currentTopic()); + break; default: console.log('Unknown command:', command); } -} \ No newline at end of file +} From 59e72998c6303748675554247e500f0f6162f128 Mon Sep 17 00:00:00 2001 From: Raven Scott Date: Fri, 14 Jun 2024 01:17:18 -0400 Subject: [PATCH 25/31] Fix message history by adding new clear function for internal command --- app.js | 13 ++++++++++++- commands.js | 2 +- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/app.js b/app.js index 4787082..4858e75 100644 --- a/app.js +++ b/app.js @@ -739,7 +739,7 @@ async function updateIcon(username, avatarBuffer) { } } -function clearMessages() { +function clearMessagesCMD() { const messagesContainer = document.querySelector('#messages'); while (messagesContainer.firstChild) { messagesContainer.removeChild(messagesContainer.firstChild); @@ -750,6 +750,17 @@ function clearMessages() { 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'); diff --git a/commands.js b/commands.js index 9d4cf57..cc289b3 100644 --- a/commands.js +++ b/commands.js @@ -20,7 +20,7 @@ export default async function handleCommand(command, context) { switch (cmd) { case '~clear': - clearMessages(); + clearMessagesCMD(); break; case '~ping': addMessage('LinkUp', 'pong', agentAvatar, currentTopic()); From f50370d2e5fe0a58683ceb143c6a263165d67445 Mon Sep 17 00:00:00 2001 From: Raven Scott Date: Fri, 14 Jun 2024 01:18:13 -0400 Subject: [PATCH 26/31] Missed something --- commands.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/commands.js b/commands.js index cc289b3..16913b5 100644 --- a/commands.js +++ b/commands.js @@ -12,7 +12,7 @@ if (fs.existsSync(agentAvatarPath)) { } export default async function handleCommand(command, context) { - const { eventEmitter, currentTopic, clearMessages, addMessage, joinRoom, leaveRoom } = context; + const { eventEmitter, currentTopic, clearMessagesCMD, addMessage, joinRoom, leaveRoom } = context; const args = command.trim().split(' '); const cmd = args[0].toLowerCase(); From 6a74c23f9ccb2b4a597eec547167503e7350c7be Mon Sep 17 00:00:00 2001 From: Raven Scott Date: Fri, 14 Jun 2024 01:19:58 -0400 Subject: [PATCH 27/31] fixed --- app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app.js b/app.js index 4858e75..a4c5b86 100644 --- a/app.js +++ b/app.js @@ -514,7 +514,7 @@ async function sendMessage(e) { await handleCommand(message, { eventEmitter, currentTopic, - clearMessages, + clearMessagesCMD, addMessage: (from, message, avatar, topic) => onMessageAdded(from, message, avatar, topic, timestamp), joinRoom, leaveRoom From f84a6cff1bbd38bb632844540da9bdac84ef12db Mon Sep 17 00:00:00 2001 From: Raven Scott Date: Fri, 14 Jun 2024 01:28:08 -0400 Subject: [PATCH 28/31] better formatting for help --- app.js | 20 ++++++++++++++------ commands.js | 13 ++++++++++--- 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/app.js b/app.js index a4c5b86..7374ac8 100644 --- a/app.js +++ b/app.js @@ -67,6 +67,13 @@ async function joinRoom(topicStr) { 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 initialize() { try { servePort = getRandomPort(); @@ -381,17 +388,17 @@ async function joinSwarm(topicBuffer) { } } -function addRoomToList(topic) { +function addRoomToList(topic, alias) { const roomList = document.querySelector('#room-list'); const roomItem = document.createElement('li'); - roomItem.textContent = truncateHash(topic); + roomItem.textContent = alias || truncateHash(topic); roomItem.dataset.topic = topic; roomItem.addEventListener('dblclick', () => enterEditMode(roomItem)); roomItem.addEventListener('click', () => switchRoom(topic)); roomList.appendChild(roomItem); - config.rooms.push({ topic, alias: truncateHash(topic) }); + config.rooms.push({ topic, alias: alias || truncateHash(topic) }); writeConfigToFile("./config.json"); } @@ -514,14 +521,15 @@ async function sendMessage(e) { await handleCommand(message, { eventEmitter, currentTopic, - clearMessagesCMD, + clearMessages, addMessage: (from, message, avatar, topic) => onMessageAdded(from, message, avatar, topic, timestamp), joinRoom, - leaveRoom + leaveRoom, + createRoom }); return; } - + console.log('Sending message:', message); // Debugging log onMessageAdded(config.userName, message, config.userAvatar, topic, timestamp); diff --git a/commands.js b/commands.js index 16913b5..2d34f2e 100644 --- a/commands.js +++ b/commands.js @@ -12,7 +12,7 @@ if (fs.existsSync(agentAvatarPath)) { } export default async function handleCommand(command, context) { - const { eventEmitter, currentTopic, clearMessagesCMD, addMessage, joinRoom, leaveRoom } = context; + const { eventEmitter, currentTopic, clearMessages, addMessage, joinRoom, leaveRoom, createRoom } = context; const args = command.trim().split(' '); const cmd = args[0].toLowerCase(); @@ -20,13 +20,13 @@ export default async function handleCommand(command, context) { switch (cmd) { case '~clear': - clearMessagesCMD(); + clearMessages(); break; case '~ping': addMessage('LinkUp', 'pong', agentAvatar, currentTopic()); break; case '~help': - addMessage('LinkUp', 'Available commands: ~clear, ~ping, ~help, ~join [topic], ~leave', agentAvatar, currentTopic()); + addMessage('LinkUp', 'Available commands:\n- ~clear\n- ~ping\n- ~help\n- ~join [topic]\n- ~leave\n- ~create [alias]', agentAvatar, currentTopic()); break; case '~join': if (restArgs) { @@ -38,6 +38,13 @@ export default async function handleCommand(command, context) { case '~leave': leaveRoom(currentTopic()); break; + case '~create': + if (restArgs) { + await createRoom(restArgs); + } else { + addMessage('LinkUp', 'Usage: ~create [alias]', agentAvatar, currentTopic()); + } + break; default: console.log('Unknown command:', command); } From 4d9d22060b450ded92e39985e3ec347600ea8c73 Mon Sep 17 00:00:00 2001 From: Raven Scott Date: Fri, 14 Jun 2024 01:56:46 -0400 Subject: [PATCH 29/31] Adding hyperdrive files management via ~list-files --- app.js | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++--- commands.js | 9 +++++++-- 2 files changed, 60 insertions(+), 5 deletions(-) diff --git a/app.js b/app.js index 7374ac8..fe94413 100644 --- a/app.js +++ b/app.js @@ -74,6 +74,17 @@ async function createRoom(alias) { await joinSwarm(topicBuffer); } +async function listFiles() { + const files = []; + for await (const entry of drive.readdir('/files')) { + files.push(entry); + } + return files; +} +async function deleteFile(filename) { + await drive.del(`/files/${filename}`); +} + async function initialize() { try { servePort = getRandomPort(); @@ -525,11 +536,13 @@ async function sendMessage(e) { addMessage: (from, message, avatar, topic) => onMessageAdded(from, message, avatar, topic, timestamp), joinRoom, leaveRoom, - createRoom + createRoom, + listFiles, + deleteFile }); return; } - + console.log('Sending message:', message); // Debugging log onMessageAdded(config.userName, message, config.userAvatar, topic, timestamp); @@ -668,6 +681,34 @@ function onMessageAdded(from, message, avatar, topic, timestamp) { 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)) { @@ -678,9 +719,9 @@ function onMessageAdded(from, message, avatar, topic, timestamp) { return ''; // use external default escaping } }); - const markdownContent = md.render(message); $text.innerHTML = markdownContent; + } $content.appendChild($header); $content.appendChild($text); @@ -693,6 +734,15 @@ function onMessageAdded(from, message, avatar, topic, timestamp) { } } +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'); diff --git a/commands.js b/commands.js index 2d34f2e..2d6b26f 100644 --- a/commands.js +++ b/commands.js @@ -12,7 +12,7 @@ if (fs.existsSync(agentAvatarPath)) { } export default async function handleCommand(command, context) { - const { eventEmitter, currentTopic, clearMessages, addMessage, joinRoom, leaveRoom, createRoom } = context; + const { eventEmitter, currentTopic, clearMessages, addMessage, joinRoom, leaveRoom, createRoom, listFiles } = context; const args = command.trim().split(' '); const cmd = args[0].toLowerCase(); @@ -26,7 +26,7 @@ export default async function handleCommand(command, context) { 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]', 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) { @@ -45,6 +45,11 @@ export default async function handleCommand(command, context) { 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()); + break; default: console.log('Unknown command:', command); } From ff0acb1a854f200490cf953b2fc0d515717bc18f Mon Sep 17 00:00:00 2001 From: Raven Scott Date: Fri, 14 Jun 2024 03:17:31 -0400 Subject: [PATCH 30/31] add readme --- README.md | 12 ++++++++++++ app.js | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..8f547ff --- /dev/null +++ b/README.md @@ -0,0 +1,12 @@ +`LinkUp` is currently in` active development` and `not fully launched`. + +To launch the App in Dev Mode: + +`git clone https://git.ssh.surf/snxraven/LinkUp-P2P-Chat.git` + +`cd LinkUp-P2P-Chat` + +`npm i; npm i pear -g;` + +Lastly - run the app: +`pear dev -s /tmp/tmp_pear` diff --git a/app.js b/app.js index fe94413..eb6a1f6 100644 --- a/app.js +++ b/app.js @@ -912,4 +912,4 @@ async function connectToAllRooms() { // Call this function when loading the rooms initially renderRoomList(); -initialize(); +initialize(); \ No newline at end of file From 50a02e8dfde846c44464e21859d08cb7500112ad Mon Sep 17 00:00:00 2001 From: Raven Scott Date: Fri, 14 Jun 2024 04:04:00 -0400 Subject: [PATCH 31/31] topic validation for the join box --- app.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/app.js b/app.js index eb6a1f6..cacbaa7 100644 --- a/app.js +++ b/app.js @@ -369,7 +369,15 @@ async function createChatRoom() { async function joinChatRoom(e) { e.preventDefault(); - const topicStr = document.querySelector('#join-chat-room-topic').value; + const topicStr = 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.'); + return; + } + const topicBuffer = b4a.from(topicStr, 'hex'); addRoomToList(topicStr); await joinSwarm(topicBuffer);