Reworked message format, added support for audio messages, made Message class #10
20
app.js
20
app.js
@ -213,6 +213,7 @@ async function handleConnection(connection, info) {
|
|||||||
type: 'icon',
|
type: 'icon',
|
||||||
username: config.userName,
|
username: config.userName,
|
||||||
avatar: b4a.toString(iconBuffer, 'base64'),
|
avatar: b4a.toString(iconBuffer, 'base64'),
|
||||||
|
timestamp: Date.now()
|
||||||
});
|
});
|
||||||
console.log('Sending icon to new peer:', iconMessage);
|
console.log('Sending icon to new peer:', iconMessage);
|
||||||
connection.write(iconMessage);
|
connection.write(iconMessage);
|
||||||
@ -289,10 +290,11 @@ function setupTalkButton() {
|
|||||||
const audioMessage = {
|
const audioMessage = {
|
||||||
type: 'audio',
|
type: 'audio',
|
||||||
name: config.userName,
|
name: config.userName,
|
||||||
audio: b4a.toString(buffer, 'base64'),
|
|
||||||
audioType: audioBlob.type,
|
|
||||||
avatar: updatePortInUrl(config.userAvatar),
|
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
|
console.log('Sending audio message:', audioMessage); // Debugging log
|
||||||
@ -563,10 +565,10 @@ async function sendMessage(e) {
|
|||||||
const messageObj = JSON.stringify({
|
const messageObj = JSON.stringify({
|
||||||
type: 'message',
|
type: 'message',
|
||||||
name: config.userName,
|
name: config.userName,
|
||||||
message,
|
|
||||||
avatar: config.userAvatar,
|
avatar: config.userAvatar,
|
||||||
topic: topic,
|
topic: topic,
|
||||||
timestamp: timestamp
|
timestamp: timestamp,
|
||||||
|
message
|
||||||
});
|
});
|
||||||
|
|
||||||
const peers = [...activeRooms.find(room => room.topic === topic).swarm.connections];
|
const peers = [...activeRooms.find(room => room.topic === topic).swarm.connections];
|
||||||
@ -590,11 +592,12 @@ async function handleFileInput(event) {
|
|||||||
const fileMessage = {
|
const fileMessage = {
|
||||||
type: 'file',
|
type: 'file',
|
||||||
name: config.userName,
|
name: config.userName,
|
||||||
|
avatar: updatePortInUrl(config.userAvatar),
|
||||||
|
topic: topic,
|
||||||
|
timestamp: Date.now(),
|
||||||
fileName: file.name,
|
fileName: file.name,
|
||||||
file: b4a.toString(buffer, 'base64'),
|
file: b4a.toString(buffer, 'base64'),
|
||||||
fileType: file.type,
|
fileType: file.type
|
||||||
avatar: updatePortInUrl(config.userAvatar),
|
|
||||||
topic: topic
|
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log('Sending file message:', fileMessage); // Debugging log
|
console.log('Sending file message:', fileMessage); // Debugging log
|
||||||
@ -847,6 +850,7 @@ function writeConfigToFile(filePath) {
|
|||||||
function updatePortInUrl(url) {
|
function updatePortInUrl(url) {
|
||||||
if (!url) return url;
|
if (!url) return url;
|
||||||
const urlObject = new URL(url);
|
const urlObject = new URL(url);
|
||||||
|
if(!urlObject.host.startsWith("localhost")) return urlObject.toString();
|
||||||
urlObject.port = servePort;
|
urlObject.port = servePort;
|
||||||
return urlObject.toString();
|
return urlObject.toString();
|
||||||
}
|
}
|
||||||
|
@ -39,10 +39,9 @@ loadCommands().then(commands => {
|
|||||||
|
|
||||||
// We use Event Emitter here to handle new messages
|
// We use Event Emitter here to handle new messages
|
||||||
bot.on('onMessage', (peer, message) => {
|
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);
|
||||||
|
|
||||||
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
|
// Check if the message starts with a command prefix
|
||||||
if (message.message.startsWith('!')) {
|
if (message.message.startsWith('!')) {
|
||||||
// Extract the command and arguments
|
// Extract the command and arguments
|
||||||
@ -64,6 +63,8 @@ loadCommands().then(commands => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
bot.joinChatRoom(process.env.LINKUP_ROOM_ID);
|
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 => {
|
}).catch(error => {
|
||||||
console.error('Error loading commands:', error);
|
console.error('Error loading commands:', error);
|
||||||
});
|
});
|
@ -1,14 +1,18 @@
|
|||||||
import Hyperswarm from 'hyperswarm';
|
import Hyperswarm from 'hyperswarm';
|
||||||
import EventEmitter from 'node:events';
|
import EventEmitter from 'node:events';
|
||||||
import b4a from "b4a";
|
import b4a from "b4a";
|
||||||
import TextMessage from "./TextMessage.js";
|
import TextMessage from "./message/TextMessage.js";
|
||||||
import FileMessage from "./FileMessage.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 {
|
class Client extends EventEmitter {
|
||||||
constructor(botName) {
|
constructor(botName) {
|
||||||
super();
|
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.botName = botName;
|
||||||
|
this.botAvatar = "";
|
||||||
this.swarm = new Hyperswarm();
|
this.swarm = new Hyperswarm();
|
||||||
this.joinedRooms = new Set(); // Track the rooms the bot has joined
|
this.joinedRooms = new Set(); // Track the rooms the bot has joined
|
||||||
this.currentTopic = null; // Track the current topic
|
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() {
|
setupSwarm() {
|
||||||
this.swarm.on('connection', (peer) => {
|
this.swarm.on('connection', (peer) => {
|
||||||
peer.on('data', message => {
|
peer.on('data', message => {
|
||||||
const messageObj = JSON.parse(message.toString());
|
const messageObj = JSON.parse(message.toString());
|
||||||
if (this.joinedRooms.has(messageObj.topic)) { // Process message only if it is from a joined room
|
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
|
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")
|
const msgType = messageObj.type;
|
||||||
this.emit('onFile', peer, new FileMessage(messageObj.name, messageObj.fileName, messageObj.fileUrl, messageObj.fileType, messageObj.avatar, messageObj.topic, messageObj.timestamp));
|
const peerName = messageObj.name;
|
||||||
|
const peerAvatar = messageObj.avatar;
|
||||||
|
const timestamp = messageObj.timestamp;
|
||||||
|
|
||||||
if (messageObj.type === "icon")
|
if (msgType === "message")
|
||||||
this.emit('onIcon', peer, messageObj);
|
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) {
|
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 data = message.toJsonString();
|
||||||
const peers = [...this.swarm.connections];
|
const peers = [...this.swarm.connections];
|
||||||
for (const peer of peers) peer.write(data);
|
for (const peer of peers) peer.write(data);
|
||||||
|
@ -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;
|
|
@ -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;
|
|
23
chatBot/includes/message/AudioMessage.js
Normal file
23
chatBot/includes/message/AudioMessage.js
Normal file
@ -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;
|
25
chatBot/includes/message/FileMessage.js
Normal file
25
chatBot/includes/message/FileMessage.js
Normal file
@ -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;
|
20
chatBot/includes/message/IconMessage.js
Normal file
20
chatBot/includes/message/IconMessage.js
Normal file
@ -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;
|
21
chatBot/includes/message/Message.js
Normal file
21
chatBot/includes/message/Message.js
Normal file
@ -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;
|
21
chatBot/includes/message/TextMessage.js
Normal file
21
chatBot/includes/message/TextMessage.js
Normal file
@ -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;
|
Loading…
Reference in New Issue
Block a user