diff --git a/app.js b/app.js index 74a6207..4a4c7a0 100644 --- a/app.js +++ b/app.js @@ -166,16 +166,23 @@ function setupEventListeners() { 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); - updateIcon(username, avatarBuffer); + try { + const avatarBuffer = b4a.from(messageObj.avatar, 'base64'); + drive.put(`/icons/${username}.png`, avatarBuffer).then(() => { + console.log(`Icon stored for user: ${username}`); // Debugging log + updateIcon(username, avatarBuffer); + }).catch(error => { + console.error(`Failed to store icon for user ${username}:`, error); + }); + } catch (error) { + console.error('Error processing avatar data:', error); + } } else { console.error('Received icon message with missing avatar data:', messageObj); } @@ -185,6 +192,8 @@ function handleIncomingMessage(messageObj) { 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); + }).catch(error => { + console.error(`Failed to store file ${messageObj.fileName}:`, error); }); } else { console.error('Received file message with missing file data or fileName:', messageObj); @@ -197,12 +206,13 @@ function handleIncomingMessage(messageObj) { drive.put(filePath, audioBuffer).then(() => { const audioUrl = `http://localhost:${servePort}${filePath}`; addAudioMessage(messageObj.name, audioUrl, messageObj.avatar, messageObj.topic); + }).catch(error => { + console.error(`Failed to store audio message:`, error); }); } else { console.error('Received unknown message type:', messageObj); } } - async function handleConnection(connection, info) { console.log('New connection', info); diff --git a/chatBot/assets/icon.png b/chatBot/assets/icon.png new file mode 100644 index 0000000..5128555 Binary files /dev/null and b/chatBot/assets/icon.png differ diff --git a/chatBot/bot.js b/chatBot/bot.js index d5eabd7..24ca968 100644 --- a/chatBot/bot.js +++ b/chatBot/bot.js @@ -52,7 +52,10 @@ loadCommands().then(commands => { // If the command exists, execute its handler if (commandHandler && typeof commandHandler.handler === 'function') { + console.log(`Executing command: ${command} with arguments: ${args}`); commandHandler.handler(bot, args, message); + } else { + console.warn(`Command not found: ${command}`); } } }); @@ -64,7 +67,9 @@ loadCommands().then(commands => { bot.joinChatRoom(process.env.LINKUP_ROOM_ID); - bot.fetchAvatar(`https://avatar.iran.liara.run/username?username=${bot.botName}&background=f4d9b2&color=FF9800&size=40`); // Debugging avatar + const iconPath = path.join(new URL('./assets/icon.png', import.meta.url).pathname); + bot.fetchAvatar(iconPath); // Fetch the avatar from local file + }).catch(error => { console.error('Error loading commands:', error); -}); \ No newline at end of file +}); diff --git a/chatBot/includes/Client.js b/chatBot/includes/Client.js index c1da18e..1311d37 100644 --- a/chatBot/includes/Client.js +++ b/chatBot/includes/Client.js @@ -6,16 +6,29 @@ import FileMessage from "./message/FileMessage.js"; import AudioMessage from "./message/AudioMessage.js"; import Message from "./message/Message.js"; import IconMessage from "./message/IconMessage.js"; +import Corestore from 'corestore'; +import Hyperdrive from 'hyperdrive'; +import fs from 'fs'; +import ServeDrive from 'serve-drive'; class Client extends EventEmitter { constructor(botName) { super(); if (!botName) return console.error("Bot Name is not defined!"); this.botName = botName; - this.botAvatar = ""; this.swarm = new Hyperswarm(); this.joinedRooms = new Set(); // Track the rooms the bot has joined this.currentTopic = null; // Track the current topic + + // Initialize Corestore and Hyperdrive + this.storagePath = './storage/'; + this.store = new Corestore(this.storagePath); + this.drive = new Hyperdrive(this.store); + + // Initialize ServeDrive + this.servePort = null; + this.initializeServeDrive(); + this.setupSwarm(); process.on('exit', () => { @@ -25,35 +38,65 @@ class Client extends EventEmitter { process.on('SIGTERM', async () => { console.log('SIGTERM signal received. Shutting down HyperSwarm...'); - await this.destroy() + await this.destroy(); console.log('HyperSwarm was shut down. Exiting the process with exit code 0.'); process.exit(0); }); process.on('SIGINT', async () => { console.log('SIGINT signal received. Shutting down HyperSwarm...'); - await this.destroy() + await this.destroy(); console.log('HyperSwarm was shut down. Exiting the process with exit code 0.'); process.exit(0); }); } - async fetchAvatar(url) { - this.botAvatar = url; - const web = await fetch(url); - const img = await web.body.getReader().read(); - this.sendMessage(IconMessage.new(this, img.value)); + async initializeServeDrive() { + try { + this.servePort = this.getRandomPort(); + const serve = new ServeDrive({ + port: this.servePort, + get: ({ key, filename, version }) => this.drive + }); + await serve.ready(); + console.log('ServeDrive listening on port:', this.servePort); + } catch (error) { + console.error('Error initializing ServeDrive:', error); + } + } + + getRandomPort() { + return Math.floor(Math.random() * (65535 - 49152 + 1)) + 49152; + } + + async fetchAvatar(filePath) { + try { + await this.drive.ready(); + const iconBuffer = fs.readFileSync(filePath); + await this.drive.put(`/icons/${this.botName}.png`, iconBuffer); + this.botAvatar = `http://localhost:${this.servePort}/icons/${this.botName}.png`; + + // Cache the icon message + this.iconMessage = IconMessage.new(this, iconBuffer); + } catch (error) { + console.error('Error fetching avatar:', error); + } } setupSwarm() { this.swarm.on('connection', (peer) => { + // Send the cached icon message to the new peer + if (this.iconMessage) { + peer.write(this.iconMessage.toJsonString()); + } + peer.on('data', message => { const messageObj = JSON.parse(message.toString()); if (this.joinedRooms.has(messageObj.topic)) { // Process message only if it is from a joined room this.currentTopic = messageObj.topic; // Set the current topic from the incoming message const msgType = messageObj.type; - const peerName = messageObj.name; + const peerName = messageObj.userName; // Changed from name to userName const peerAvatar = messageObj.avatar; const timestamp = messageObj.timestamp; @@ -73,7 +116,7 @@ class Client extends EventEmitter { peer.on('error', e => { this.emit('onError', e); - console.error(`Connection error: ${e}`) + console.error(`Connection error: ${e}`); }); }); @@ -97,17 +140,33 @@ class Client extends EventEmitter { } sendTextMessage(message) { + console.log(`Preparing to send text message: ${message}`); this.sendMessage(TextMessage.new(this, message)); } sendMessage(message) { - if(!(message instanceof Message)) return console.log(`message does not extend Message class (TextMessage, FileMessage, AudioMessage).`, message); + if (!(message instanceof Message)) { + console.error(`message does not extend Message class (TextMessage, FileMessage, AudioMessage).`, message); + return; + } - // console.log('Bot name:', this.botName); console.log("Sending message:", message); const data = message.toJsonString(); const peers = [...this.swarm.connections]; - for (const peer of peers) peer.write(data); + if (peers.length === 0) { + console.warn("No active peer connections found."); + return; + } + + console.log(`Sending message to ${peers.length} peers.`); + for (const peer of peers) { + try { + peer.write(data); + console.log(`Message sent to peer: ${peer.remoteAddress}`); + } catch (error) { + console.error(`Failed to send message to peer: ${peer.remoteAddress}`, error); + } + } } async destroy() { diff --git a/chatBot/includes/message/IconMessage.js b/chatBot/includes/message/IconMessage.js index bb4afc4..f3e9bd5 100644 --- a/chatBot/includes/message/IconMessage.js +++ b/chatBot/includes/message/IconMessage.js @@ -2,8 +2,8 @@ import Message from "./Message.js"; import b4a from "b4a"; class IconMessage extends Message { - constructor(peerName, peerAvatar, topic, timestamp) { - super("icon", peerName, peerAvatar, topic, timestamp); + constructor(peerName, peerAvatar, timestamp) { + super("icon", peerName, peerAvatar, null, timestamp); } toJsonString() { diff --git a/chatBot/includes/message/Message.js b/chatBot/includes/message/Message.js index fbb2fff..3a2c9c0 100644 --- a/chatBot/includes/message/Message.js +++ b/chatBot/includes/message/Message.js @@ -10,7 +10,7 @@ class Message { toJson() { return { type: this.type, - name: this.peerName, + username: this.peerName, avatar: this.peerAvatar, topic: this.topic, timestamp: this.timestamp diff --git a/chatBot/includes/message/TextMessage.js b/chatBot/includes/message/TextMessage.js index 716cde1..f4a68c0 100644 --- a/chatBot/includes/message/TextMessage.js +++ b/chatBot/includes/message/TextMessage.js @@ -1,21 +1,21 @@ import Message from "./Message.js"; class TextMessage extends Message { - constructor(peerName, peerAvatar, topic, timestamp, message) { - super("message", peerName, peerAvatar, topic, timestamp); - this.message = message; - } + constructor(peerName, peerAvatar, topic, timestamp, message) { + super("message", peerName, peerAvatar, topic, timestamp); + this.message = message; + } - toJsonString() { - return JSON.stringify({ - ...this.toJson(), - message: this.message, - }); - } + toJsonString() { + return JSON.stringify({ + ...this.toJson(), + message: this.message, + }); + } - static new(bot, message) { - return new TextMessage(bot.botName, bot.botAvatar, bot.currentTopic, Date.now(), message); - } + static new(bot, message) { + return new TextMessage(bot.botName, bot.botAvatar, bot.currentTopic, Date.now(), message); + } } export default TextMessage;