diff --git a/app.js b/app.js
index b1df6c3..72dcc80 100644
--- a/app.js
+++ b/app.js
@@ -5,7 +5,7 @@ import ServeDrive from 'serve-drive';
import Hyperdrive from 'hyperdrive';
import Corestore from 'corestore';
import { EventEmitter } from 'events';
-import fs from "fs";
+import fs from 'fs';
import handleCommand from './commands.js';
const agentAvatarPath = './assets/agent.png';
@@ -32,7 +32,7 @@ let servePort;
let config = {
userName: '',
userAvatar: '',
- rooms: [],
+ guilds: {},
registeredUsers: {}
};
@@ -44,8 +44,8 @@ function getRandomPort() {
return Math.floor(Math.random() * (65535 - 49152 + 1)) + 49152;
}
-function currentTopic() {
- return document.querySelector('#chat-room-topic').innerText;
+function currentGuildTopic() {
+ return document.querySelector('#chat-guild-topic').innerText;
}
function getCurrentPeerCount() {
@@ -61,17 +61,57 @@ function updatePeerCount() {
}
}
-async function joinRoom(topicStr) {
- const topicBuffer = b4a.from(topicStr, 'hex');
- addRoomToList(topicStr);
+async function processGuild(guildData) {
+ const parsedData = JSON.parse(guildData);
+ config.guilds[parsedData.guildTopic] = {
+ alias: parsedData.guildAlias,
+ rooms: parsedData.rooms,
+ owner: parsedData.owner
+ };
+ writeConfigToFile("./config.json");
+ renderGuildList();
+ await joinGuild(parsedData.guildTopic);
+}
+
+async function joinGuild(guildTopic) {
+ const guild = config.guilds[guildTopic];
+ if (guild) {
+ for (const room of guild.rooms) {
+ await joinRoom(guildTopic, room.topic, room.alias);
+ }
+ }
+}
+
+async function joinRoom(guildTopic, roomTopic, alias) {
+ const topicBuffer = b4a.from(roomTopic, 'hex');
+ addRoomToList(guildTopic, roomTopic, alias);
await joinSwarm(topicBuffer);
}
-async function createRoom(alias) {
- const topicBuffer = crypto.randomBytes(32);
- const topic = b4a.toString(topicBuffer, 'hex');
- addRoomToList(topic, alias);
- await joinSwarm(topicBuffer);
+async function createRoom(guildTopic, alias) {
+ const roomTopicBuffer = crypto.randomBytes(32);
+ const roomTopic = b4a.toString(roomTopicBuffer, 'hex');
+
+ config.guilds[guildTopic].rooms.push({ topic: roomTopic, alias: alias || truncateHash(roomTopic) });
+ writeConfigToFile("./config.json");
+
+ addRoomToList(guildTopic, roomTopic, alias);
+ await joinSwarm(roomTopicBuffer);
+
+ // Synchronize the new room with other peers
+ const roomMessage = JSON.stringify({
+ type: 'room',
+ guildTopic,
+ room: { topic: roomTopic, alias: alias || truncateHash(roomTopic) }
+ });
+
+ const guildSwarm = activeRooms.find(room => room.topic === guildTopic);
+ if (guildSwarm) {
+ const peers = [...guildSwarm.swarm.connections];
+ for (const peer of peers) {
+ peer.write(roomMessage);
+ }
+ }
}
async function listFiles() {
@@ -81,6 +121,7 @@ async function listFiles() {
}
return files;
}
+
async function deleteFile(filename) {
await drive.del(`/files/${filename}`);
}
@@ -98,7 +139,7 @@ async function initialize() {
const configExists = fs.existsSync("./config.json");
if (configExists) {
loadConfigFromFile();
- renderRoomList();
+ renderGuildList();
await connectToAllRooms();
}
@@ -113,6 +154,31 @@ async function initialize() {
document.addEventListener("DOMContentLoaded", (event) => {
hljs.highlightAll();
});
+
+ document.addEventListener('createGuild', (event) => {
+ const { guildName } = event.detail;
+ createGuild(guildName);
+ });
+
+ document.addEventListener('addRoomToGuild', (event) => {
+ const { guildTopic, roomName } = event.detail;
+ createRoom(guildTopic, roomName);
+ });
+
+ document.addEventListener('manageGuild', (event) => {
+ const { guildTopic } = event.detail;
+ openManageGuildModal(guildTopic);
+ });
+
+ document.addEventListener('switchRoom', (event) => {
+ const { guildTopic, roomTopic } = event.detail;
+ if (!roomTopic) {
+ console.error('Invalid room topic:', roomTopic);
+ return;
+ }
+ console.log('Event switchRoom with roomTopic:', roomTopic);
+ switchRoom(guildTopic, roomTopic);
+ });
} catch (error) {
console.error('Error during initialization:', error);
}
@@ -139,7 +205,10 @@ function setupEventListeners() {
});
}
if (createChatRoomButton) {
- createChatRoomButton.addEventListener('click', createChatRoom);
+ createChatRoomButton.addEventListener('click', () => {
+ const guildTopic = currentGuildTopic();
+ createRoom(guildTopic);
+ });
}
if (joinChatRoomButton) {
joinChatRoomButton.addEventListener('click', joinChatRoom);
@@ -165,7 +234,21 @@ function setupEventListeners() {
if (talkButton) {
setupTalkButton();
}
+
+ // Add event listeners only for room items
+ document.querySelectorAll('.room-item').forEach(item => {
+ item.addEventListener('click', () => {
+ const guildTopic = item.dataset.guildTopic;
+ const roomTopic = item.dataset.topic;
+ if (!roomTopic) {
+ console.error('Invalid room topic for item:', item);
+ return;
+ }
+ switchRoom(guildTopic, roomTopic);
+ });
+ });
}
+
function handleIncomingMessage(messageObj) {
console.log('Received message:', messageObj); // Debugging log
@@ -209,10 +292,58 @@ function handleIncomingMessage(messageObj) {
}).catch(error => {
console.error(`Failed to store audio message:`, error);
});
+ } else if (messageObj.type === 'guild') {
+ const guildData = messageObj.guildData;
+ processGuild(guildData);
+ } else if (messageObj.type === 'room') {
+ const { guildTopic, room } = messageObj;
+ if (config.guilds[guildTopic]) {
+ config.guilds[guildTopic].rooms.push(room);
+ writeConfigToFile("./config.json");
+ renderGuildList();
+ joinRoom(guildTopic, room.topic, room.alias);
+ }
+ } else if (messageObj.type === 'remove-room') {
+ const { guildTopic, roomTopic } = messageObj;
+ if (config.guilds[guildTopic]) {
+ const roomIndex = config.guilds[guildTopic].rooms.findIndex(room => room.topic === roomTopic);
+ if (roomIndex !== -1) {
+ config.guilds[guildTopic].rooms.splice(roomIndex, 1);
+ writeConfigToFile("./config.json");
+ renderGuildList();
+ leaveRoom(roomTopic);
+ }
+ }
+ } else if (messageObj.type === 'rename-room') {
+ const { guildTopic, roomTopic, newAlias } = messageObj;
+ if (config.guilds[guildTopic]) {
+ const room = config.guilds[guildTopic].rooms.find(room => room.topic === roomTopic);
+ if (room) {
+ room.alias = newAlias;
+ writeConfigToFile("./config.json");
+
+ // Synchronize the room rename with other peers
+ const renameMessage = JSON.stringify({
+ type: 'rename-room',
+ guildTopic,
+ roomTopic: topic,
+ newAlias
+ });
+
+ const guildSwarm = activeRooms.find(room => room.topic === guildTopic);
+ if (guildSwarm) {
+ const peers = [...guildSwarm.swarm.connections];
+ for (const peer of peers) {
+ peer.write(renameMessage);
+ }
+ }
+ }
+ }
} else {
console.error('Received unknown message type:', messageObj);
}
}
+
async function handleConnection(connection, info) {
console.log('New connection', info);
@@ -231,6 +362,22 @@ async function handleConnection(connection, info) {
console.error('Icon not found for user:', config.userName);
}
+ // Sending the guilds and rooms information
+ for (const guildTopic in config.guilds) {
+ const guild = config.guilds[guildTopic];
+ const guildMessage = JSON.stringify({
+ type: 'guild',
+ guildData: JSON.stringify({
+ guildTopic,
+ guildAlias: guild.alias,
+ rooms: guild.rooms,
+ owner: guild.owner
+ })
+ });
+ console.log('Sending guild information to new peer:', guildMessage);
+ connection.write(guildMessage);
+ }
+
connection.on('data', (data) => {
const messageObj = JSON.parse(data.toString());
eventEmitter.emit('onMessage', messageObj);
@@ -377,27 +524,37 @@ async function continueRegistration(regUsername) {
writeConfigToFile("./config.json");
}
-async function createChatRoom() {
+async function createGuild(guildName) {
const topicBuffer = crypto.randomBytes(32);
const topic = b4a.toString(topicBuffer, 'hex');
- addRoomToList(topic);
- await joinSwarm(topicBuffer);
+
+ config.guilds[topic] = {
+ alias: guildName,
+ rooms: [],
+ owner: config.userName
+ };
+
+ addGuildToList(topic, guildName);
+ writeConfigToFile("./config.json");
}
async function joinChatRoom(e) {
e.preventDefault();
- const topicStr = document.querySelector('#join-chat-room-topic').value.trim();
+ const guildTopic = document.querySelector('#join-guild-topic').value.trim();
+ const roomTopic = document.querySelector('#join-chat-room-topic').value.trim();
// Validate the topic string
- const isValidTopic = /^[0-9a-fA-F]{64}$/.test(topicStr);
- if (!isValidTopic) {
- alert('Invalid topic format. Please enter a 64-character hexadecimal string.');
+ const isValidGuildTopic = /^[0-9a-fA-F]{64}$/.test(guildTopic);
+ const isValidRoomTopic = /^[0-9a-fA-F]{64}$/.test(roomTopic);
+ if (!isValidGuildTopic || !isValidRoomTopic) {
+ alert('Invalid topic format. Please enter 64-character hexadecimal strings.');
return;
}
- const topicBuffer = b4a.from(topicStr, 'hex');
- addRoomToList(topicStr);
- await joinSwarm(topicBuffer);
+ const guildTopicBuffer = b4a.from(guildTopic, 'hex');
+ const roomTopicBuffer = b4a.from(roomTopic, 'hex');
+ addRoomToList(guildTopic, roomTopic);
+ await joinSwarm(roomTopicBuffer);
}
async function joinSwarm(topicBuffer) {
@@ -424,18 +581,100 @@ async function joinSwarm(topicBuffer) {
}
}
-function addRoomToList(topic, alias) {
- const roomList = document.querySelector('#room-list');
- const roomItem = document.createElement('li');
- roomItem.textContent = alias || truncateHash(topic);
- roomItem.dataset.topic = topic;
+function addGuildToList(guildTopic, alias) {
+ const guildList = document.querySelector('#guild-list');
+ const guildItem = document.createElement('li');
+ guildItem.textContent = alias || truncateHash(guildTopic);
+ guildItem.dataset.guildTopic = guildTopic;
+ guildItem.classList.add('guild-item');
- roomItem.addEventListener('dblclick', () => enterEditMode(roomItem));
- roomItem.addEventListener('click', () => switchRoom(topic));
- roomList.appendChild(roomItem);
+ if (config.guilds[guildTopic].owner === config.userName) {
+ const manageButton = document.createElement('button');
+ manageButton.textContent = 'Manage';
+ manageButton.classList.add('mini-button', 'manage-guild-btn');
+ manageButton.addEventListener('click', (e) => {
+ e.stopPropagation();
+ openManageGuildModal(guildTopic);
+ });
+ guildItem.appendChild(manageButton);
+ }
- config.rooms.push({ topic, alias: alias || truncateHash(topic) });
- writeConfigToFile("./config.json");
+ const roomList = document.createElement('ul');
+ roomList.classList.add('room-list');
+ guildItem.appendChild(roomList);
+
+ guildList.appendChild(guildItem);
+
+ // Add the rooms for this guild
+ config.guilds[guildTopic].rooms.forEach(room => {
+ addRoomToList(guildTopic, room.topic, room.alias);
+ });
+}
+
+function addRoomToList(guildTopic, roomTopic, alias) {
+ const guildItem = document.querySelector(`li[data-guild-topic="${guildTopic}"]`);
+ if (guildItem) {
+ let roomList = guildItem.querySelector('.room-list');
+ if (!roomList) {
+ roomList = document.createElement('ul');
+ roomList.classList.add('room-list');
+ guildItem.appendChild(roomList);
+ }
+
+ if (!roomList.querySelector(`li[data-topic="${roomTopic}"]`)) {
+ const roomItem = document.createElement('li');
+ roomItem.textContent = alias || truncateHash(roomTopic);
+ roomItem.dataset.topic = roomTopic;
+ roomItem.dataset.guildTopic = guildTopic;
+ roomItem.classList.add('room-item');
+
+ roomItem.addEventListener('dblclick', () => enterEditMode(roomItem));
+ roomItem.addEventListener('click', () => switchRoom(guildTopic, roomTopic));
+ roomList.appendChild(roomItem);
+ }
+ }
+}
+
+function openManageGuildModal(guildTopic) {
+ const guild = config.guilds[guildTopic];
+ if (!guild) return;
+
+ const manageGuildModal = document.getElementById('manage-guild-modal');
+ manageGuildModal.dataset.guildTopic = guildTopic;
+
+ const guildInfo = manageGuildModal.querySelector('#guild-info');
+ guildInfo.innerHTML = `
${guild.alias}
`;
+
+ const roomList = manageGuildModal.querySelector('#room-list');
+ roomList.innerHTML = '';
+
+ guild.rooms.forEach(room => {
+ const roomItem = document.createElement('li');
+ roomItem.textContent = room.alias;
+ roomItem.dataset.topic = room.topic;
+
+ const editIcon = document.createElement('span');
+ editIcon.textContent = '✏️';
+ editIcon.classList.add('edit-icon');
+ editIcon.addEventListener('click', (e) => {
+ e.stopPropagation();
+ enterEditMode(roomItem);
+ });
+
+ const deleteIcon = document.createElement('span');
+ deleteIcon.textContent = '❌';
+ deleteIcon.classList.add('delete-icon');
+ deleteIcon.addEventListener('click', (e) => {
+ e.stopPropagation();
+ removeRoom(guildTopic, room.topic);
+ });
+
+ roomItem.appendChild(editIcon);
+ roomItem.appendChild(deleteIcon);
+ roomList.appendChild(roomItem);
+ });
+
+ manageGuildModal.classList.remove('hidden');
}
function enterEditMode(roomItem) {
@@ -472,10 +711,31 @@ function exitEditMode(roomItem, input, topic) {
roomItem.textContent = newAlias;
// Update the config with the new alias
- const roomConfig = config.rooms.find(room => room.topic === topic);
- if (roomConfig) {
- roomConfig.alias = newAlias;
- writeConfigToFile("./config.json");
+ for (const guildTopic in config.guilds) {
+ const guild = config.guilds[guildTopic];
+ const room = guild.rooms.find(room => room.topic === topic);
+ if (room) {
+ room.alias = newAlias;
+ writeConfigToFile("./config.json");
+
+ // Synchronize the room rename with other peers
+ const renameMessage = JSON.stringify({
+ type: 'rename-room',
+ guildTopic,
+ roomTopic: topic,
+ newAlias
+ });
+
+ const guildSwarm = activeRooms.find(room => room.topic === guildTopic);
+ if (guildSwarm) {
+ const peers = [...guildSwarm.swarm.connections];
+ for (const peer of peers) {
+ peer.write(renameMessage);
+ }
+ }
+
+ break;
+ }
}
// Check if the edited room is the current room in view
@@ -490,40 +750,83 @@ function exitEditMode(roomItem, input, topic) {
}
}
-function switchRoom(topic) {
- console.log('Switching to room:', topic); // Debugging log
+function removeRoom(guildTopic, roomTopic) {
+ const guild = config.guilds[guildTopic];
+ if (guild) {
+ const roomIndex = guild.rooms.findIndex(room => room.topic === roomTopic);
+ if (roomIndex !== -1) {
+ guild.rooms.splice(roomIndex, 1);
+ writeConfigToFile("./config.json");
+ renderGuildList();
+ leaveRoom(roomTopic);
+
+ // Synchronize the room removal with other peers
+ const removeMessage = JSON.stringify({
+ type: 'remove-room',
+ guildTopic,
+ roomTopic
+ });
+
+ const guildSwarm = activeRooms.find(room => room.topic === guildTopic);
+ if (guildSwarm) {
+ const peers = [...guildSwarm.swarm.connections];
+ for (const peer of peers) {
+ peer.write(removeMessage);
+ }
+ }
+ }
+ }
+}
+
+function switchRoom(guildTopic, roomTopic) {
+ if (!roomTopic) {
+ console.error('Invalid room topic:', roomTopic);
+ return;
+ }
+
+ console.log('Switching to room:', roomTopic);
const chatRoomTopic = document.querySelector('#chat-room-topic');
const chatRoomName = document.querySelector('#chat-room-name');
+ const guild = config.guilds[guildTopic];
+
+ if (!guild) {
+ console.error('Guild not found:', guildTopic);
+ return;
+ }
if (chatRoomTopic) {
- chatRoomTopic.innerText = topic; // Set full topic here
+ chatRoomTopic.innerText = roomTopic; // Set full topic here
} else {
console.error('Element #chat-room-topic not found');
}
if (chatRoomName) {
- // Update the room name in the header
- const room = config.rooms.find(r => r.topic === topic);
- const roomName = room ? room.alias : truncateHash(topic);
- chatRoomName.innerText = roomName;
+ // Find the room in the current guild
+ const room = guild.rooms.find(room => room.topic === roomTopic);
+ if (room) {
+ // Update the room name in the header
+ chatRoomName.innerText = room.alias;
+ } else {
+ console.error('Room not found in the current guild:', roomTopic);
+ }
} else {
console.error('Element #chat-room-name not found');
}
- clearMessages();
- renderMessagesForRoom(topic);
- updatePeerCount();
-
- // Show chat UI elements
+ // Show the chat view
document.querySelector('#chat').classList.remove('hidden');
document.querySelector('#setup').classList.add('hidden');
+
+ // Render the messages for the room
+ renderMessagesForRoom(roomTopic);
}
-function leaveRoom(topic) {
+async function leaveRoom(topic) {
const roomIndex = activeRooms.findIndex(room => room.topic === topic);
if (roomIndex !== -1) {
- const room = activeRooms[roomIndex];
- room.swarm.destroy();
+ const { swarm, discovery } = activeRooms[roomIndex];
+ await discovery.destroy();
+ swarm.destroy();
activeRooms.splice(roomIndex, 1);
}
@@ -532,17 +835,237 @@ function leaveRoom(topic) {
roomItem.remove();
}
- config.rooms = config.rooms.filter(e => e.topic !== topic);
- writeConfigToFile("./config.json");
+ const messagesContainer = document.querySelector('#messages');
+ if (messagesContainer) {
+ messagesContainer.innerHTML = '';
+ }
- if (activeRooms.length > 0) {
- switchRoom(activeRooms[0].topic);
- } else {
- document.querySelector('#chat').classList.add('hidden');
- document.querySelector('#setup').classList.remove('hidden');
+ const chatRoomName = document.querySelector('#chat-room-name');
+ if (chatRoomName) {
+ chatRoomName.innerText = '';
+ }
+
+ const chatRoomTopic = document.querySelector('#chat-room-topic');
+ if (chatRoomTopic) {
+ chatRoomTopic.innerText = '';
+ }
+
+ const chatDiv = document.querySelector('#chat');
+ const setupDiv = document.querySelector('#setup');
+ if (chatDiv && setupDiv) {
+ chatDiv.classList.add('hidden');
+ setupDiv.classList.remove('hidden');
}
}
+function writeConfigToFile(path) {
+ fs.writeFileSync(path, JSON.stringify(config, null, 2));
+}
+
+function loadConfigFromFile() {
+ const configFile = fs.readFileSync("./config.json", 'utf8');
+ config = JSON.parse(configFile);
+}
+
+function renderGuildList() {
+ const guildList = document.querySelector('#guild-list');
+ guildList.innerHTML = '';
+
+ for (const guildTopic in config.guilds) {
+ const guild = config.guilds[guildTopic];
+ addGuildToList(guildTopic, guild.alias);
+ }
+}
+
+async function connectToAllRooms() {
+ for (const guildTopic in config.guilds) {
+ const guild = config.guilds[guildTopic];
+ for (const room of guild.rooms) {
+ await joinRoom(guildTopic, room.topic, room.alias);
+ }
+ }
+}
+
+function toggleSetupView() {
+ const setupDiv = document.querySelector('#setup');
+ const chatDiv = document.querySelector('#chat');
+ setupDiv.classList.toggle('hidden');
+ chatDiv.classList.toggle('hidden');
+}
+
+function truncateHash(hash) {
+ return `${hash.substring(0, 4)}...${hash.substring(hash.length - 4)}`;
+}
+
+function updatePortInUrl(url) {
+ if (typeof url !== 'string') {
+ console.error('Invalid URL format:', url);
+ return '';
+ }
+ const urlObj = new URL(url);
+ urlObj.port = servePort;
+ return urlObj.toString();
+}
+
+function addFileMessage(name, fileName, fileUrl, fileType, avatar, topic) {
+ const container = document.querySelector('#messages');
+ if (!container) {
+ console.error('Element #messages not found');
+ return;
+ }
+
+ const messageDiv = document.createElement('div');
+ messageDiv.classList.add('message');
+ if (topic !== currentTopic()) {
+ messageDiv.classList.add('hidden'); // Hide messages not belonging to the current room
+ }
+
+ const avatarImg = document.createElement('img');
+ avatarImg.src = updatePortInUrl(avatar);
+ avatarImg.alt = `${name}'s avatar`;
+ avatarImg.classList.add('avatar');
+
+ const messageContent = document.createElement('div');
+ messageContent.classList.add('message-content');
+
+ const senderName = document.createElement('div');
+ senderName.classList.add('message-sender');
+ senderName.textContent = name;
+
+ const fileLink = document.createElement('a');
+ fileLink.href = fileUrl;
+ fileLink.textContent = `File: ${fileName}`;
+ fileLink.classList.add('message-file');
+
+ if (fileType.startsWith('image/')) {
+ const filePreview = document.createElement('img');
+ filePreview.src = fileUrl;
+ filePreview.alt = fileName;
+ filePreview.classList.add('file-preview');
+ messageContent.appendChild(filePreview);
+ } else if (fileType.startsWith('video/')) {
+ const filePreview = document.createElement('video');
+ filePreview.src = fileUrl;
+ filePreview.alt = fileName;
+ filePreview.classList.add('file-preview');
+ filePreview.controls = true;
+ messageContent.appendChild(filePreview);
+ } else if (fileType.startsWith('audio/')) {
+ const filePreview = document.createElement('audio');
+ filePreview.src = fileUrl;
+ filePreview.alt = fileName;
+ filePreview.classList.add('file-preview');
+ filePreview.controls = true;
+ messageContent.appendChild(filePreview);
+ } else {
+ const filePreview = document.createElement('div');
+ filePreview.textContent = 'No preview available';
+ filePreview.classList.add('file-preview');
+ messageContent.appendChild(filePreview);
+ }
+
+ messageContent.appendChild(senderName);
+ messageContent.appendChild(fileLink);
+ messageDiv.appendChild(avatarImg);
+ messageDiv.appendChild(messageContent);
+ container.appendChild(messageDiv);
+
+ if (topic === currentTopic()) {
+ container.scrollTop = container.scrollHeight;
+ }
+}
+
+function addAudioMessage(name, audioUrl, avatar, topic) {
+ const container = document.querySelector('#messages');
+ if (!container) {
+ console.error('Element #messages not found');
+ return;
+ }
+
+ const messageDiv = document.createElement('div');
+ messageDiv.classList.add('message');
+ if (topic !== currentTopic()) {
+ messageDiv.classList.add('hidden'); // Hide messages not belonging to the current room
+ }
+
+ const avatarImg = document.createElement('img');
+ avatarImg.src = updatePortInUrl(avatar);
+ avatarImg.alt = `${name}'s avatar`;
+ avatarImg.classList.add('avatar');
+
+ const messageContent = document.createElement('div');
+ messageContent.classList.add('message-content');
+
+ const senderName = document.createElement('div');
+ senderName.classList.add('message-sender');
+ senderName.textContent = name;
+
+ const audioElement = document.createElement('audio');
+ audioElement.src = audioUrl;
+ audioElement.controls = true;
+ audioElement.classList.add('message-audio');
+
+ messageContent.appendChild(senderName);
+ messageContent.appendChild(audioElement);
+ messageDiv.appendChild(avatarImg);
+ messageDiv.appendChild(messageContent);
+ container.appendChild(messageDiv);
+
+ if (topic === currentTopic()) {
+ container.scrollTop = container.scrollHeight;
+ }
+}
+
+function addMessage(name, message, avatar, topic) {
+ const container = document.querySelector('#messages');
+ if (!container) {
+ console.error('Element #messages not found');
+ return;
+ }
+
+ const messageDiv = document.createElement('div');
+ messageDiv.classList.add('message');
+ if (topic !== currentTopic()) {
+ messageDiv.classList.add('hidden'); // Hide messages not belonging to the current room
+ }
+
+ const avatarImg = document.createElement('img');
+ avatarImg.src = updatePortInUrl(avatar);
+ avatarImg.alt = `${name}'s avatar`;
+ avatarImg.classList.add('avatar');
+
+ const messageContent = document.createElement('div');
+ messageContent.classList.add('message-content');
+
+ const senderName = document.createElement('div');
+ senderName.classList.add('message-sender');
+ senderName.textContent = name;
+
+ const messageText = document.createElement('div');
+ messageText.classList.add('message-text');
+ messageText.innerHTML = message;
+
+ messageContent.appendChild(senderName);
+ messageContent.appendChild(messageText);
+ messageDiv.appendChild(avatarImg);
+ messageDiv.appendChild(messageContent);
+ container.appendChild(messageDiv);
+
+ if (topic === currentTopic()) {
+ container.scrollTop = container.scrollHeight;
+ }
+}
+
+function clearMessages() {
+ const messagesContainer = document.querySelector('#messages');
+ while (messagesContainer.firstChild) {
+ messagesContainer.removeChild(messagesContainer.firstChild);
+ }
+}
+
+function currentTopic() {
+ return document.querySelector('#chat-room-topic').innerText;
+}
async function sendMessage(e) {
e.preventDefault();
@@ -552,13 +1075,14 @@ async function sendMessage(e) {
const topic = currentTopic();
const timestamp = Date.now();
+ console.log("Sending message to current topic:", topic); // Add logging
+
if (message.startsWith('~')) {
- // Handle command
await handleCommand(message, {
eventEmitter,
- currentTopic,
+ currentTopic: topic, // Pass the current topic as a string
clearMessages,
- addMessage: (from, message, avatar, topic) => onMessageAdded(from, message, avatar, topic, timestamp),
+ addMessage,
joinRoom,
leaveRoom,
createRoom,
@@ -587,31 +1111,57 @@ async function sendMessage(e) {
}
}
-async function handleFileInput(event) {
+function onMessageAdded(name, message, avatar, topic, timestamp) {
+ if (!messagesStore[topic]) {
+ messagesStore[topic] = [];
+ }
+ messagesStore[topic].push({ name, message, avatar, timestamp });
+
+ const chatRoomTopic = currentTopic();
+ if (topic === chatRoomTopic) {
+ addMessage(name, message, avatar, topic);
+ }
+}
+
+function renderMessagesForRoom(topic) {
+ const container = document.querySelector('#messages');
+ if (!container) {
+ console.error('Element #messages not found');
+ return;
+ }
+
+ container.innerHTML = '';
+
+ if (!messagesStore[topic]) return;
+
+ messagesStore[topic].forEach(({ name, message, avatar }) => {
+ addMessage(name, message, avatar, topic);
+ });
+}
+
+function handleFileInput(event) {
const file = event.target.files[0];
if (file) {
const reader = new FileReader();
- reader.onload = async (event) => {
+ reader.onload = async function(event) {
const buffer = new Uint8Array(event.target.result);
const filePath = `/files/${file.name}`;
await drive.put(filePath, buffer);
- const fileUrl = `http://localhost:${servePort}${filePath}`;
+ const fileUrl = `http://localhost:${servePort}/files/${file.name}`;
const topic = currentTopic();
const fileMessage = {
type: 'file',
name: config.userName,
- avatar: updatePortInUrl(config.userAvatar),
- topic: topic,
- timestamp: Date.now(),
fileName: file.name,
file: b4a.toString(buffer, 'base64'),
- fileType: file.type
+ fileType: file.type,
+ avatar: updatePortInUrl(config.userAvatar),
+ topic: topic,
+ timestamp: Date.now()
};
- console.log('Sending file message:', fileMessage); // Debugging log
-
const peers = [...activeRooms.find(room => room.topic === topic).swarm.connections];
for (const peer of peers) {
peer.write(JSON.stringify(fileMessage));
@@ -623,323 +1173,4 @@ async function handleFileInput(event) {
}
}
-function addFileMessage(from, fileName, fileUrl, fileType, avatar, topic) {
- console.log('Adding file message:', { from, fileName, fileUrl, fileType, avatar, topic }); // Debugging log
- const $div = document.createElement('div');
- $div.classList.add('message');
-
- const $img = document.createElement('img');
- $img.src = updatePortInUrl(avatar) || 'https://via.placeholder.com/40';
- $img.classList.add('avatar');
- $img.draggable = false;
- $div.appendChild($img);
-
- const $content = document.createElement('div');
- $content.classList.add('message-content');
-
- const $header = document.createElement('div');
- $header.classList.add('message-header');
- $header.textContent = from;
- $content.appendChild($header);
-
- if (fileType.startsWith('image/')) {
- const $image = document.createElement('img');
- $image.src = updatePortInUrl(fileUrl);
- $image.alt = fileName;
- $image.classList.add('attached-image');
- $content.appendChild($image);
- } else {
- const $fileButton = document.createElement('button');
- $fileButton.textContent = `Download File: ${fileName}`;
- $fileButton.onclick = function() {
- const $fileLink = document.createElement('a');
- $fileLink.href = fileUrl;
- $fileLink.download = fileName;
- $fileLink.click();
- };
- $content.appendChild($fileButton);
- }
-
- $div.appendChild($content);
-
- // Only render the message if it's for the current room
- if (currentTopic() === topic) {
- document.querySelector('#messages').appendChild($div);
- scrollToBottom();
- } else {
- console.log(`Message for topic ${topic} not rendered because current topic is ${currentTopic()}`); // Debugging log
- }
-}
-
-function scrollToBottom() {
- var container = document.getElementById("messages-container");
- container.scrollTop = container.scrollHeight;
-}
-
-function onMessageAdded(from, message, avatar, topic, timestamp) {
- console.log('Adding message:', { from, message, avatar, topic }); // Debugging log
- const messageObj = {
- from,
- message,
- avatar,
- timestamp: timestamp || Date.now()
- };
-
- // Add the message to the store
- addMessageToStore(topic, messageObj);
-
- // Only render messages for the current room
- if (currentTopic() === topic) {
- const $div = document.createElement('div');
- $div.classList.add('message');
-
- const $img = document.createElement('img');
- $img.src = updatePortInUrl(avatar) || 'https://via.placeholder.com/40'; // Default to a placeholder image if avatar URL is not provided
- $img.classList.add('avatar');
- $img.draggable = false;
- $div.appendChild($img);
-
- const $content = document.createElement('div');
- $content.classList.add('message-content');
-
- const $header = document.createElement('div');
- $header.classList.add('message-header');
- $header.textContent = from;
-
- const $text = document.createElement('div');
- $text.classList.add('message-text');
-
- if (message.includes('Available files:')) {
- const files = message.split('\n').slice(1); // Skip the "Available files:" line
- const fileList = document.createElement('ul');
-
- files.forEach(file => {
- file = file.replace("- ", "")
- const listItem = document.createElement('li');
- const fileButton = document.createElement('button');
- fileButton.textContent = file.trim();
- fileButton.onclick = () => downloadFile(file.trim());
-
- const deleteButton = document.createElement('button');
- deleteButton.textContent = 'Delete';
- deleteButton.onclick = () => {
- console.log("file to delete: ", file);
- deleteFile(file);
- listItem.remove();
- };
-
-
- listItem.appendChild(fileButton);
- listItem.appendChild(deleteButton);
- fileList.appendChild(listItem);
- });
-
- $text.appendChild(fileList);
- } else {
-
- const md = window.markdownit({
- highlight: function (str, lang) {
- if (lang && hljs.getLanguage(lang)) {
- try {
- return hljs.highlight(str, { language: lang }).value;
- } catch (__) {}
- }
- return ''; // use external default escaping
- }
- });
- const markdownContent = md.render(message);
- $text.innerHTML = markdownContent;
- }
-
- $content.appendChild($header);
- $content.appendChild($text);
- $div.appendChild($content);
-
- document.querySelector('#messages').appendChild($div);
- scrollToBottom();
- } else {
- console.log(`Message for topic ${topic} not rendered because current topic is ${currentTopic()}`); // Debugging log
- }
-}
-
-function downloadFile(filename) {
- const fileUrl = `http://localhost:${servePort}/files/${filename}`;
- const a = document.createElement('a');
- a.href = fileUrl;
- a.download = filename;
- a.click();
-}
-
-
-function addAudioMessage(from, audioUrl, avatar, topic) {
- console.log('Adding audio message:', { from, audioUrl, avatar, topic }); // Debugging log
- const $div = document.createElement('div');
- $div.classList.add('message');
-
- const $img = document.createElement('img');
- $img.src = updatePortInUrl(avatar) || 'https://via.placeholder.com/40';
- $img.classList.add('avatar');
- $img.draggable = false;
- $div.appendChild($img);
-
- const $content = document.createElement('div');
- $content.classList.add('message-content');
-
- const $header = document.createElement('div');
- $header.classList.add('message-header');
- $header.textContent = from;
- $content.appendChild($header);
-
- const $audio = document.createElement('audio');
- $audio.controls = true;
- if (from !== config.userName) {
- $audio.autoplay = true; // Add autoplay attribute for peers only
- }
- $audio.src = updatePortInUrl(audioUrl);
- $audio.classList.add('attached-audio');
- $content.appendChild($audio);
-
- $div.appendChild($content);
-
- // Only render the message if it's for the current room
- if (currentTopic() === topic) {
- document.querySelector('#messages').appendChild($div);
- scrollToBottom();
- } else {
- console.log(`Message for topic ${topic} not rendered because current topic is ${currentTopic()}`); // Debugging log
- }
-}
-
-function truncateHash(hash) {
- return `${hash.slice(0, 6)}...${hash.slice(-6)}`;
-}
-
-async function updateIcon(username, avatarBuffer) {
- const userIcon = document.querySelector(`img[src*="${username}.png"]`);
- if (userIcon) {
- const avatarBlob = new Blob([avatarBuffer], { type: 'image/png' });
- const avatarUrl = URL.createObjectURL(avatarBlob);
- userIcon.src = updatePortInUrl(avatarUrl);
-
- config.userAvatar = avatarUrl;
- writeConfigToFile("./config.json");
- }
-}
-
-function clearMessagesCMD() {
- const messagesContainer = document.querySelector('#messages');
- while (messagesContainer.firstChild) {
- messagesContainer.removeChild(messagesContainer.firstChild);
- }
-
- // Clear the messages from the store for the current room
- const topic = currentTopic();
- messagesStore[topic] = [];
-}
-
-function clearMessages() {
- const messagesContainer = document.querySelector('#messages');
- while (messagesContainer.firstChild) {
- messagesContainer.removeChild(messagesContainer.firstChild);
- }
-
- // Clear the messages from the store for the current room
- // const topic = currentTopic();
- // messagesStore[topic] = [];
-}
-
-function toggleSetupView() {
- const setupDiv = document.querySelector('#setup');
- setupDiv.classList.toggle('hidden');
-}
-
-function writeConfigToFile(filePath) {
- fs.writeFile(filePath, JSON.stringify(config), (err) => {
- if (err) return console.error(err);
- console.log("File has been created");
- });
-}
-
-function updatePortInUrl(url) {
- if (!url) return url;
- const urlObject = new URL(url);
- if(!urlObject.host.startsWith("localhost")) return urlObject.toString();
- urlObject.port = servePort;
- return urlObject.toString();
-}
-
-function renderRoomList() {
- const roomList = document.querySelector('#room-list');
- roomList.innerHTML = '';
-
- config.rooms.forEach(room => {
- const roomItem = document.createElement('li');
- roomItem.textContent = room.alias || truncateHash(room.topic);
- roomItem.dataset.topic = room.topic;
-
- roomItem.addEventListener('dblclick', () => enterEditMode(roomItem));
- roomItem.addEventListener('click', () => switchRoom(room.topic));
- roomList.appendChild(roomItem);
- });
-}
-
-function renderMessagesForRoom(topic) {
- console.log('Rendering messages for room:', topic); // Debugging log
- // Clear the message area
- clearMessages();
-
- // Fetch and render messages for the selected room
- const messages = getMessagesForRoom(topic);
- messages.forEach(message => {
- onMessageAdded(message.from, message.message, message.avatar, topic, message.timestamp);
- });
-}
-
-function getMessagesForRoom(topic) {
- return messagesStore[topic] || [];
-}
-
-function addMessageToStore(topic, messageObj) {
- if (!messagesStore[topic]) {
- messagesStore[topic] = [];
- }
-
- // Check for duplicates using a combination of message content and timestamp
- const isDuplicate = messagesStore[topic].some(msg =>
- msg.from === messageObj.from &&
- msg.message === messageObj.message &&
- msg.timestamp === messageObj.timestamp
- );
-
- if (!isDuplicate) {
- messagesStore[topic].push(messageObj);
- } else {
- console.log('Duplicate message detected:', messageObj); // Debugging log
- }
-}
-
-function loadConfigFromFile() {
- config = JSON.parse(fs.readFileSync("./config.json", 'utf8'));
- console.log("Read config from file:", config);
- // Update port in URLs
- config.userAvatar = updatePortInUrl(config.userAvatar);
- config.rooms.forEach(room => {
- room.alias = room.alias || truncateHash(room.topic);
- });
- for (let user in config.registeredUsers) {
- config.registeredUsers[user] = updatePortInUrl(config.registeredUsers[user]);
- }
-}
-
-async function connectToAllRooms() {
- // Connect to all rooms on startup
- for (const room of config.rooms) {
- const topicBuffer = b4a.from(room.topic, 'hex');
- await joinSwarm(topicBuffer);
- }
-}
-
-// Call this function when loading the rooms initially
-renderRoomList();
-
initialize();
diff --git a/chatBot/commands/ping.js b/chatBot/commands/ping.js
index 8ba0a84..d7566cd 100644
--- a/chatBot/commands/ping.js
+++ b/chatBot/commands/ping.js
@@ -1,7 +1,7 @@
export default {
handler: function(bot, args, message) {
// Specify the path to the file you want to send
- const filePath = '/to/file/path.js'; // Replace with the actual file path
+ const filePath = '/Users/raven/chat/chatBot/bot.js'; // Replace with the actual file path
const fileType = 'text/html'; // Specify the correct file type
// Send the file message using the bot instance
diff --git a/commands.js b/commands.js
index 2d6b26f..3c2241c 100644
--- a/commands.js
+++ b/commands.js
@@ -1,4 +1,3 @@
-// commands.js
import b4a from 'b4a';
import fs from 'fs';
@@ -13,44 +12,50 @@ if (fs.existsSync(agentAvatarPath)) {
export default async function handleCommand(command, context) {
const { eventEmitter, currentTopic, clearMessages, addMessage, joinRoom, leaveRoom, createRoom, listFiles } = context;
-
+
+ console.log("Context received in handleCommand:", context); // Add logging
+
const args = command.trim().split(' ');
const cmd = args[0].toLowerCase();
const restArgs = args.slice(1).join(' ');
+ console.log("Command received:", cmd); // Add logging
+ console.log("Current topic:", currentTopic); // Add logging to check the current topic
+
switch (cmd) {
case '~clear':
- clearMessages();
+ clearMessages(currentTopic);
break;
case '~ping':
- addMessage('LinkUp', 'pong', agentAvatar, currentTopic());
+ addMessage('LinkUp', 'pong', agentAvatar, currentTopic);
break;
case '~help':
- addMessage('LinkUp', 'Available commands:\n- ~clear\n- ~ping\n- ~help\n- ~join [topic]\n- ~leave\n- ~create [alias]\n- ~list-files', agentAvatar, currentTopic());
+ addMessage('LinkUp', 'Available commands:\n- ~clear\n- ~ping\n- ~help\n- ~join [topic]\n- ~leave\n- ~create [alias]\n- ~list-files', agentAvatar, currentTopic);
break;
case '~join':
if (restArgs) {
- await joinRoom(restArgs);
+ await joinRoom(currentTopic, restArgs);
} else {
- addMessage('LinkUp', 'Usage: ~join [topic]', agentAvatar, currentTopic());
+ addMessage('LinkUp', 'Usage: ~join [topic]', agentAvatar, currentTopic);
}
break;
case '~leave':
- leaveRoom(currentTopic());
+ leaveRoom(currentTopic);
break;
case '~create':
if (restArgs) {
- await createRoom(restArgs);
+ await createRoom(currentTopic, restArgs);
} else {
- addMessage('LinkUp', 'Usage: ~create [alias]', agentAvatar, currentTopic());
+ addMessage('LinkUp', 'Usage: ~create [alias]', agentAvatar, currentTopic);
}
break;
case '~list-files':
const files = await listFiles();
const fileList = files.length > 0 ? files.map(file => `- ${file}`).join('\n') : 'No files available';
- addMessage('LinkUp', `Available files:\n${fileList}`, agentAvatar, currentTopic());
+ addMessage('LinkUp', `Available files:\n${fileList}`, agentAvatar, currentTopic);
break;
default:
+ addMessage('LinkUp', `Unknown command: ${cmd}`, agentAvatar, currentTopic);
console.log('Unknown command:', command);
}
}
diff --git a/index.html b/index.html
index d918404..8f539cb 100644
--- a/index.html
+++ b/index.html
@@ -17,10 +17,10 @@
@@ -35,12 +35,12 @@
-
+
- or -
-
-
-
-
@@ -62,7 +61,7 @@
-
+
@@ -70,11 +69,46 @@
Loading ...
+
+
+
+
+ ×
+
Create Guild
+
+
+
+
+
+
+
+
×
+
Manage Guild
+
+
+
+
+
+
+
+
+