import Hyperswarm from 'hyperswarm'; import EventEmitter from 'node:events'; import b4a from "b4a"; import TextMessage from "./TextMessage.js"; import FileMessage from "./FileMessage.js"; import AudioMessage from "./AudioMessage.js"; import Message from "./Message.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 this.setupSwarm(); process.on('exit', () => { console.log('EXIT signal received. Shutting down HyperSwarm...'); this.destroy(); }); process.on('SIGTERM', async () => { console.log('SIGTERM signal received. Shutting down HyperSwarm...'); 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() console.log('HyperSwarm was shut down. Exiting the process with exit code 0.'); process.exit(0); }); } 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.timestamp, messageObj.message)); if (messageObj.type === "file") this.emit('onFile', peer, new FileMessage(messageObj.name, messageObj.avatar, messageObj.fileName, messageObj.fileUrl, messageObj.fileType, messageObj.avatar, messageObj.topic, messageObj.timestamp)); if (messageObj.type === "icon") this.emit('onIcon', peer, messageObj); if (messageObj.type === "audio") this.emit('onAudio', peer, new AudioMessage()); } }); peer.on('error', e => { this.emit('onError', e); console.error(`Connection error: ${e}`) }); }); this.swarm.on('update', () => { console.log(`Connections count: ${this.swarm.connections.size} / Peers count: ${this.swarm.peers.size}`); }); } 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 }); this.discovery.flushed().then(() => { console.log(`Bot ${this.botName} joined the chat room.`); this.emit('onBotJoinRoom'); }); } sendTextMessage(message) { this.sendMessage(TextMessage.new(this, message)); } sendMessage(message) { console.log(message); console.log(Message.isPrototypeOf(message)); console.log('Bot name:', this.botName); const data = message.toJsonString(); const peers = [...this.swarm.connections]; for (const peer of peers) peer.write(data); } async destroy() { await this.swarm.destroy(); console.log(`Bot ${this.botName} disconnected.`); } } export default Client;