diff --git a/app.js b/app.js index b3a2a12..74a6207 100644 --- a/app.js +++ b/app.js @@ -213,6 +213,7 @@ async function handleConnection(connection, info) { type: 'icon', username: config.userName, avatar: b4a.toString(iconBuffer, 'base64'), + timestamp: Date.now() }); console.log('Sending icon to new peer:', iconMessage); connection.write(iconMessage); @@ -289,10 +290,11 @@ function setupTalkButton() { const audioMessage = { type: 'audio', name: config.userName, - audio: b4a.toString(buffer, 'base64'), - audioType: audioBlob.type, avatar: updatePortInUrl(config.userAvatar), - topic: topic + topic: topic, + timestamp: Date.now(), + audio: b4a.toString(buffer, 'base64'), + audioType: audioBlob.type }; console.log('Sending audio message:', audioMessage); // Debugging log @@ -563,10 +565,10 @@ async function sendMessage(e) { const messageObj = JSON.stringify({ type: 'message', name: config.userName, - message, avatar: config.userAvatar, topic: topic, - timestamp: timestamp + timestamp: timestamp, + message }); const peers = [...activeRooms.find(room => room.topic === topic).swarm.connections]; @@ -590,11 +592,12 @@ async function handleFileInput(event) { 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, - avatar: updatePortInUrl(config.userAvatar), - topic: topic + fileType: file.type }; console.log('Sending file message:', fileMessage); // Debugging log @@ -847,6 +850,7 @@ function writeConfigToFile(filePath) { 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(); } diff --git a/chatBot/bot.js b/chatBot/bot.js index d13f001..d5eabd7 100644 --- a/chatBot/bot.js +++ b/chatBot/bot.js @@ -39,10 +39,9 @@ loadCommands().then(commands => { // We use Event Emitter here to handle new messages bot.on('onMessage', (peer, message) => { + console.log(`Message received from ${message.peerName} at ${new Date(message.timestamp).toLocaleTimeString()}: ${message.message}`); console.log(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 @@ -64,6 +63,8 @@ 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 }).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 dbc5c08..c1da18e 100644 --- a/chatBot/includes/Client.js +++ b/chatBot/includes/Client.js @@ -1,14 +1,18 @@ import Hyperswarm from 'hyperswarm'; import EventEmitter from 'node:events'; import b4a from "b4a"; -import TextMessage from "./TextMessage.js"; -import FileMessage from "./FileMessage.js"; +import TextMessage from "./message/TextMessage.js"; +import FileMessage from "./message/FileMessage.js"; +import AudioMessage from "./message/AudioMessage.js"; +import Message from "./message/Message.js"; +import IconMessage from "./message/IconMessage.js"; 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 @@ -34,20 +38,36 @@ class Client extends EventEmitter { }); } + 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)); + } + setupSwarm() { this.swarm.on('connection', (peer) => { 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 - if (messageObj.type === "message") - this.emit('onMessage', peer, new TextMessage(messageObj.name, messageObj.avatar, messageObj.topic, messageObj.message, messageObj.timestamp)); - if (messageObj.type === "file") - this.emit('onFile', peer, new FileMessage(messageObj.name, messageObj.fileName, messageObj.fileUrl, messageObj.fileType, messageObj.avatar, messageObj.topic, messageObj.timestamp)); + const msgType = messageObj.type; + const peerName = messageObj.name; + const peerAvatar = messageObj.avatar; + const timestamp = messageObj.timestamp; - if (messageObj.type === "icon") - this.emit('onIcon', peer, messageObj); + if (msgType === "message") + this.emit('onMessage', peer, new TextMessage(peerName, peerAvatar, this.currentTopic, timestamp, messageObj.message)); + + if (msgType === "file") + this.emit('onFile', peer, new FileMessage(peerName, peerAvatar, this.currentTopic, timestamp, messageObj.fileName, messageObj.fileUrl, messageObj.fileType)); + + if (msgType === "icon") + this.emit('onIcon', peer, new IconMessage(peerName, peerAvatar, timestamp)); + + if (msgType === "audio") + this.emit('onAudio', peer, new AudioMessage(peerName, peerAvatar, this.currentTopic, timestamp, messageObj.audio, messageObj.audioType)); } }); @@ -81,7 +101,10 @@ class Client extends EventEmitter { } sendMessage(message) { - console.log('Bot name:', this.botName); + if(!(message instanceof Message)) return console.log(`message does not extend Message class (TextMessage, FileMessage, AudioMessage).`, message); + + // 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); diff --git a/chatBot/includes/FileMessage.js b/chatBot/includes/FileMessage.js deleted file mode 100644 index aa055fe..0000000 --- a/chatBot/includes/FileMessage.js +++ /dev/null @@ -1,26 +0,0 @@ -class FileMessage { - constructor(peerName, fileName, fileUrl, fileType, peerAvatar, topic, timestamp) { - this.peerName = peerName; - this.fileName = fileName; - this.fileUrl = fileUrl; - this.fileType = fileType; - this.peerAvatar = peerAvatar; - this.topic = topic; - this.timestamp = timestamp; - } - - toJsonString() { - return JSON.stringify({ - type: 'file', - name: this.peerName, - fileName: this.fileName, - fileUrl: this.fileUrl, - fileType: this.fileType, - avatar: this.peerAvatar, - topic: this.topic, - timestamp: this.timestamp, - }); - } -} - -export default FileMessage; diff --git a/chatBot/includes/TextMessage.js b/chatBot/includes/TextMessage.js deleted file mode 100644 index fa07783..0000000 --- a/chatBot/includes/TextMessage.js +++ /dev/null @@ -1,26 +0,0 @@ -class TextMessage { - constructor(peerName, peerAvatar, topic, message, timestamp) { - this.peerName = peerName; - this.peerAvatar = peerAvatar; - this.topic = topic; - this.message = message; - this.timestamp = timestamp; - } - - toJsonString() { - return JSON.stringify({ - type: 'message', - name: this.peerName, - message: this.message, - avatar: this.peerAvatar, - topic: this.topic, - timestamp: this.timestamp - }); - } - - static new(bot, message) { - return new TextMessage(bot.botName, "", bot.currentTopic, message, Date.now()); - } -} - -export default TextMessage; diff --git a/chatBot/includes/message/AudioMessage.js b/chatBot/includes/message/AudioMessage.js new file mode 100644 index 0000000..2fcb8a0 --- /dev/null +++ b/chatBot/includes/message/AudioMessage.js @@ -0,0 +1,23 @@ +import Message from "./Message.js"; + +class AudioMessage extends Message { + constructor(peerName, peerAvatar, topic, timestamp, audio, audioType) { + super("audio", peerName, peerAvatar, topic, timestamp); + this.audio = audio; + this.audioType = audioType; + } + + toJsonString() { + return JSON.stringify({ + ...this.toJson(), + audio: this.audio, + audioType: this.audioType + }); + } + + static new(bot, audio, audioType) { + return new AudioMessage(bot.botName, bot.botAvatar, bot.currentTopic, Date.now(), audio, audioType); + } +} + +export default AudioMessage; diff --git a/chatBot/includes/message/FileMessage.js b/chatBot/includes/message/FileMessage.js new file mode 100644 index 0000000..d62ff7b --- /dev/null +++ b/chatBot/includes/message/FileMessage.js @@ -0,0 +1,25 @@ +import Message from "./Message.js"; + +class FileMessage extends Message { + constructor(peerName, peerAvatar, topic, timestamp, fileName, fileUrl, fileType) { + super("file", peerName, peerAvatar, topic, timestamp); + this.fileName = fileName; + this.fileUrl = fileUrl; + this.fileType = fileType; + } + + toJsonString() { + return JSON.stringify({ + ...this.toJson(), + fileName: this.fileName, + fileUrl: this.fileUrl, + fileType: this.fileType + }); + } + + static new(bot, fileName, fileUrl, fileType) { + return new FileMessage(bot.botName, bot.botAvatar, bot.currentTopic, Date.now(), fileName, fileUrl, fileType); + } +} + +export default FileMessage; diff --git a/chatBot/includes/message/IconMessage.js b/chatBot/includes/message/IconMessage.js new file mode 100644 index 0000000..bb4afc4 --- /dev/null +++ b/chatBot/includes/message/IconMessage.js @@ -0,0 +1,20 @@ +import Message from "./Message.js"; +import b4a from "b4a"; + +class IconMessage extends Message { + constructor(peerName, peerAvatar, topic, timestamp) { + super("icon", peerName, peerAvatar, topic, timestamp); + } + + toJsonString() { + return JSON.stringify({ + ...this.toJson() + }); + } + + static new(bot, avatarBuffer) { + return new IconMessage(bot.botName, b4a.toString(avatarBuffer, 'base64'), Date.now()); + } +} + +export default IconMessage; diff --git a/chatBot/includes/message/Message.js b/chatBot/includes/message/Message.js new file mode 100644 index 0000000..fbb2fff --- /dev/null +++ b/chatBot/includes/message/Message.js @@ -0,0 +1,21 @@ +class Message { + constructor(messageType, peerName, peerAvatar, topic, timestamp) { + this.type = messageType; + this.peerName = peerName; + this.peerAvatar = peerAvatar; + this.topic = topic; + this.timestamp = timestamp; + } + + toJson() { + return { + type: this.type, + name: this.peerName, + avatar: this.peerAvatar, + topic: this.topic, + timestamp: this.timestamp + }; + } +} + +export default Message; diff --git a/chatBot/includes/message/TextMessage.js b/chatBot/includes/message/TextMessage.js new file mode 100644 index 0000000..716cde1 --- /dev/null +++ b/chatBot/includes/message/TextMessage.js @@ -0,0 +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; + } + + 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); + } +} + +export default TextMessage;