UNPKG

@trap_stevo/filetide

Version:

Revolutionizing real-time file transfer with seamless, instant communication across any device. Deliver files instantly, regardless of platform, and experience unparalleled speed and control in managing transfers. Elevate your file-sharing capabilities wi

753 lines (744 loc) 33.5 kB
"use strict"; var _FileTide; function _classPrivateMethodInitSpec(e, a) { _checkPrivateRedeclaration(e, a), a.add(e); } function _checkPrivateRedeclaration(e, t) { if (t.has(e)) throw new TypeError("Cannot initialize the same private elements twice on an object"); } function _assertClassBrand(e, t, n) { if ("function" == typeof e ? e === t : e.has(t)) return arguments.length < 3 ? t : n; throw new TypeError("Private element is not present on this object"); } const { HUDUtilityManager } = require("@trap_stevo/legendarybuilderpronodejs-utilities"); const chalk = require("chalk"); const path = require("path"); const fs = require("fs"); const os = require("os"); const { FileTideDebugUtilityManager, significantMessageColors, errorMessageColors } = require("./HUDManagers/FileTideDebugUtilityManager.js"); const { FileTransferTrackingManager } = require("./HUDManagers/FileTransferTrackingManager.js"); const { FileTransferReceiverManager } = require("./HUDManagers/FileTransferReceiverManager.js"); const FileTransferRecoveryManager = require("./HUDManagers/FileTransferRecoveryManager.js"); const { FileNetConfigManager } = require("./HUDManagers/FileNetConfigManager.js"); const { FileUtilityManager } = require("./HUDManagers/FileUtilityManager.js"); const ConsoleProgressBarManager = require("./HUDComponents/ConsoleProgressBarManager"); const ChunkCapacitor = require("./HUDComponents/ChunkCapacitor.js"); const ConsoleTable = require("./HUDComponents/ConsoleTable.js"); const FileMessagerClient = require("./FileMessagerClient"); const FileMessager = require("./FileMessager"); var _FileTide_brand = /*#__PURE__*/new WeakSet(); class FileTide { constructor(debugMode = true) { /** * Set up file transfer event listeners natively. * Automatically checks and creates directories as needed. */ _classPrivateMethodInitSpec(this, _FileTide_brand); this.progressBarManager = new ConsoleProgressBarManager([], { completionMessageColor: chalk.blue.bold }); this.clientShorePolicies = new Map(); this.transferBarrier = false; this.clientDetails = new Map(); this.clients = new Map(); this.currentUserID = ""; this.runningOn = os.platform(); this.inDebugMode = debugMode; FileTideDebugUtilityManager.setDebugMode(debugMode); return; } static outputGradient(text, colors) { FileTideDebugUtilityManager.outputGradient(text, colors); return; } /** * Initialize and start the server. * @param {Object} serverOptions - Configuration for server (e.g., port, useCors, useHTTPS) * @param {Function} onLaunch - Callback for when a file tide instance launches * @param {...any} tideConfigurations - Additional configuration objects for customizing FileTide behavior */ launchFileTide(serverOptions = {}, onLaunch, ...tideConfigurations) { this.fileNet = new FileMessager(serverOptions, ...tideConfigurations); this.fileNet.start(); FileTide.outputGradient("[FileTide] ~ FileNet launched successfully!", significantMessageColors); if (onLaunch) { onLaunch(this.fileNet); } return; } /** * Initialize and start a new client for each user. * @param {Object} clientOptions - Configuration for client (e.g., serverUrl) * @param {Object} connectionOptions - Configuration for the client connection * @param {Object} transportOptions - Configuration for the file transporter * @param {Object} messagerSettings - Configuration for the messager * @param {String} roomName - The name of the room to join * @param {String} userID - The ID of the client user * @param {Function} onLaunch - Callback for when a file messager launches * @param {Function} onIncomingFile - Callback for incoming files in transit * @param {Function} onIncomingTransfer - Callback for incoming transfers * @param {Function} onTransferBarrier - Callback for when an incoming transfer status updates * @param {Function} onTransferProgress - Callback for when an current transfers progress * @param {Boolean} [enableTransferBarrier=true] - Whether to enable the transfer barrier * @param {Object} [options = {}] - Additional options * @param {Object} [headers = {}] - Additional connection headers * @param {Function} onConnect - Callback for when the client connects to the desired server * @param {Function} onDisconnect - Callback for when the client disconnects from the desired server * @param {String} authURL - The destination of the connection authentication functionality * @param {Object} [authHeaders = {}] - Additional connection authentication headers * @param {Boolean} [useAuthentication=true] - Whether to enable connection authentication * @param {Boolean} [queryAuthToken=false] - Whether to enable connection authentication tokens */ launchMessager(clientOptions = {}, connectionOptions = {}, transportOptions = { parallelChunks: 3, maxRetries: 3 }, messagerSettings = { maxReconnectionAttempts: 15, reconnectionAttempts: 15, maxReconnectionDelay: 10000 }, roomName, userID, onLaunch, onIncomingFile, onIncomingTransfer, onTransferBarrier, onTransferProgress, enableTransferBarrier = true, options = {}, headers = {}, onConnect = null, onDisconnect = null, authURL = "", authHeaders = {}, useAuthentication = false, queryAuthToken = false) { if (this.clients.has(userID)) { this.stopMessager(userID); } const currentClientDetails = this.clientDetails.get(userID); if (currentClientDetails && currentClientDetails.unauthorized) { const { onCurrentClientUnauthorized } = options; FileTide.outputGradient(`[FileTide ~ FileNet] ~ Unauthorized access detected.`, errorMessageColors); if (onCurrentClientUnauthorized) { onCurrentClientUnauthorized({ ...currentClientDetails, userID }); } return; } const newClient = new FileMessagerClient(clientOptions, transportOptions, messagerSettings); const enterRoom = newClient.joinRoom(userID, roomName, connectionOptions, headers, onConnect, onDisconnect, authURL, authHeaders, useAuthentication, queryAuthToken); this.transferBarrier = enableTransferBarrier; this.clientShorePolicies.set(userID, [FileUtilityManager.getTidePath(">Downloads"), FileUtilityManager.getTidePath(">Documents"), FileUtilityManager.getTidePath(">Desktop"), FileUtilityManager.getTidePath(">Photos"), FileUtilityManager.getTidePath(">Videos"), FileUtilityManager.getTidePath(">Music")]); this.clients.set(userID, newClient); this.clientDetails.set(userID, { unauthorized: false, userID }); FileTide.outputGradient(`[FileTide] ~ Messager launched successfully for client ~ ${userID}!`, significantMessageColors); _assertClassBrand(_FileTide_brand, this, _setupFileEventListeners).call(this, userID, newClient, onTransferProgress, onIncomingTransfer, onIncomingFile, onTransferBarrier, options); if (onLaunch) { onLaunch(newClient, enterRoom); } return; } async getDirectoryData(directoryPath) { return await FileUtilityManager.getDirectoryData(directoryPath); } getFileData(filePath) { return FileUtilityManager.getFileData(filePath); } async getPathData(inputPath, filterContent = []) { return await FileUtilityManager.getPathData(inputPath, filterContent); } getTidePath(inputPath) { return FileUtilityManager.getTidePath(inputPath); } getTidingTransferStates() { return FileTransferRecoveryManager.memoryStorage; } saveTransferStates() { FileTransferRecoveryManager.flushToDisk(); } filterFilesAndDirectories(dirPath, filterContent = [], callback) { FileUtilityManager.filterFilesAndDirectories(dirPath, filterContent, (files, error) => { if (callback) { callback(files, error); } }); } listFilesFromClient(recipientID, depth = 1) { const clientID = this.currentUserID; const clientMessager = this.clients.get(clientID); if (!clientMessager) { FileTide.outputGradient(`[FileTide] Client ~ ${clientID} not found.`, errorMessageColors); return; } clientMessager.clientTide.emitEvent(clientID, "check-client-banned", { clientID, tideID: clientMessager.clientTide.sockets[clientID].tideID }); clientMessager.listFiles(clientID, recipientID, depth); } async listFiles(directories, depth = 1, onDirectory) { return await FileUtilityManager.listFiles(directories, depth, onDirectory); } clearShorePolicies(clientID, shores = []) { let clientShorePolicies = this.clientShorePolicies.get(clientID); if (!shores || shores.length <= 0 || !clientShorePolicies || !clientID) { FileTide.outputGradient(`[FileTide] Did not clear from client ~ ${clientID} shore policies.`, errorMessageColors); return false; } const shorePolicies = Array.from(clientShorePolicies.values()); const currentShores = new Set(clientShorePolicies); this.clientShorePolicies.set(clientID, shorePolicies.filter(shorePolicy => !currentShores.has(shorePolicy))); return true; } addShorePolicies(clientID, shores = []) { const clientShorePolicies = this.clientShorePolicies.get(clientID); if (!shores || shores.length <= 0 || !clientShorePolicies || !clientID) { FileTide.outputGradient(`[FileTide] Did not add to client ~ ${clientID} shore policies.`, errorMessageColors); return false; } this.clientShorePolicies.set(clientID, [...new Set([...clientShorePolicies, ...shores])]); return true; } getShorePolicies(clientID) { return this.clientShorePolicies.get(clientID); } /** * Send files / directories directly to a device. * @param {String} recipientID - The ID of the client device to send the data to. * @param {Buffer} fileData - The path of the data to send. * @param {String} destinationPath - The destination path on the device. * @param {Array} filterDirectoryContent - List of paths to filter. * @param {Number} tideSize - The tide size in sending smaller to medium files (200). * @param {Number} tidalSize - The tide size in sending large files (500). * @param {Number} minTideSize - The minimum tide size in sending files (10). * @param {Number} maxTideSize - The maximum tide size in sending files (2000). * @param {Number} transferRequestExpirationTime - The maximum time until the transfer barrier times out the transfer request. */ async sendToDevice(recipientID, filePath, destinationPath, filterDirectoryContent = [], tideSize = 200, tidalSize = 500, minTideSize = 10, maxTideSize = 2000, transferRequestExpirationTime = 30000) { const clientID = this.currentUserID; const clientMessager = this.clients.get(clientID); if (!clientMessager) { FileTide.outputGradient(`[FileTide] Client ~ ${clientID} not found.`, errorMessageColors); return false; } clientMessager.clientTide.emitEvent(clientID, "check-client-banned", { clientID, tideID: clientMessager.clientTide.sockets[clientID].tideID }); const recipientOnline = await clientMessager.clientTide.emitEventWithResponse(clientID, "check-client-online", "retrieved-client-online", { clientID: recipientID }, 5000); if (!recipientOnline || !recipientOnline.online) { FileTide.outputGradient(`[FileTide] ${recipientID} currently not tiding.`, errorMessageColors); return false; } const fileDetails = await this.getPathData(filePath, filterDirectoryContent); if (!fileDetails) { FileTide.outputGradient(`[${clientID}] File at path ${filePath} not found.`, errorMessageColors); return false; } if (fileDetails.type === "file") { if (fileDetails.largeFile) { FileTide.outputGradient(`[${clientID}] Sending large file ~ ${fileDetails.fileName} to client ~ ${recipientID}...`, significantMessageColors); await clientMessager.sendFile(clientID, recipientID, fileDetails.fileName, fileDetails.filePath, destinationPath, fileDetails.fileSize, tidalSize, minTideSize, maxTideSize, true, false, true, transferRequestExpirationTime); return true; } FileTide.outputGradient(`[${clientID}] Sending file ~ ${fileDetails.content.fileName} to client ~ ${recipientID}...`, significantMessageColors); await clientMessager.sendFile(clientID, recipientID, fileDetails.content.fileName, fileDetails.content.filePath, destinationPath, fileDetails.content.size, tideSize, minTideSize, maxTideSize, transferRequestExpirationTime); return true; } if (fileDetails.type === "directory") { FileTide.outputGradient(`[${clientID}] Sending ${fileDetails.content.length} files in directory ~ ${path.basename(filePath)} to client ~ ${recipientID}...`, significantMessageColors); await clientMessager.sendDirectoryFiles(clientID, recipientID, fileDetails.content, this.getTidePath(filePath), destinationPath, tideSize, minTideSize, maxTideSize, transferRequestExpirationTime); return true; } FileTide.outputGradient(`[FileTide] ~ No active client ~ ${recipientID} found.`, errorMessageColors); return false; } /** * Check if running in server mode. * @returns {Boolean} - True if running as a server */ fileTideOperational() { return this.fileNet !== null; } /** * Check if running in client mode. * @returns {Boolean} - True if any client is active */ messagerOperational() { return this.clients.size > 0; } /** * Stop the server if running. */ stopFileTide() { if (this.fileNet) { FileTide.outputGradient("Stopping FileTide...", significantMessageColors); this.fileNet.close(); this.fileNet = null; FileTide.outputGradient("Stopped FileTide.", significantMessageColors); } return; } /** * Stop a specific client. * @param {String} userID - The ID of the client user to stop * @param {Function} onBeforeDisconnect - Executes before messager disconnection * @param {Function} onDisconnect - Executes upon messager disconnection * @param {Function} onCompletion - Executes upon messager disconnection completion */ async stopMessager(userID, onBeforeDisconnect, onDisconnect, onCompletion) { if (this.clients.has(userID)) { FileTide.outputGradient(`Stopping messager for user ${userID}...`, significantMessageColors); const connectionOrigin = this.clients.get(userID); if (typeof onBeforeDisconnect === "function") { await onBeforeDisconnect(this, userID, connectionOrigin); } if (connectionOrigin?.clientTide?.closeSocket) { const result = connectionOrigin.clientTide.closeSocket(userID); if (result instanceof Promise) { try { await result; } catch (error) { FileTide.outputGradient(`Did not disconnect ~ ${userID} ~ ${error.message}`, errorMessageColors); } } if (typeof onDisconnect === "function") { await onDisconnect(this, userID, connectionOrigin); } } this.clients.delete(userID); FileTide.outputGradient(`Stopped messager for user ${userID}.`, significantMessageColors); if (typeof onCompletion === "function") { await onCompletion(this, userID, connectionOrigin); } return; } FileTide.outputGradient(`No active messager found for user ${userID}.`, errorMessageColors); return; } /** * Stop all active clients. * @param {Function} onBeforeClientDisconnect - Executes before each messager disconnection * @param {Function} onClientDisconnect - Executes upon each messager disconnection * @param {Function} onCompletion - Executes upon all messager disconnection */ async stopAllMessagers(onBeforeClientDisconnect, onClientDisconnect, onCompletion) { FileTide.outputGradient("Stopping all messagers...", significantMessageColors); const disconnectTasks = []; for (let [userID, connectionOrigin] of this.clients.entries()) { if (typeof onBeforeClientDisconnect === "function") { await onBeforeClientDisconnect(this, userID, connectionOrigin); } if (connectionOrigin?.clientTide?.closeSocket) { const task = async () => { try { const result = connectionOrigin.clientTide.closeSocket(userID); if (result instanceof Promise) { await result; } if (typeof onClientDisconnect === "function") { await onClientDisconnect(this, userID, connectionOrigin); } FileTide.outputGradient(`Disconnected ~ ${userID}`, significantMessageColors); } catch (error) { FileTide.outputGradient(`Did not disconnect ~ ${userID} - ${error.message}`, errorMessageColors); } }; disconnectTasks.push(task()); } } await Promise.all(disconnectTasks); this.clients.clear(); FileTide.outputGradient("All messagers stopped.", significantMessageColors); if (typeof onCompletion === "function") { await onCompletion(this); } return; } } _FileTide = FileTide; function _setupFileEventListeners(userID, client, onTransferProgress, onIncomingTransfer, onIncomingFile, onTransferBarrier, options = {}) { const { onTransferStart, onTransferComplete, onTransferRequested, onTransferStatus, onListFiles, onFileListReceived, onCurrentOnlineClients, onClientTiding, onCurrentClientIDAlreadyOnline, onCurrentClientUnauthorized, onBeforeCurrentClientUnauthorizedDisconnect, onCurrentClientUnauthorizedDisconnect } = options; const cliTable = new ConsoleTable({ padding: 2, headerAlign: "center", cellAlign: "left", borderStyle: "bold", columnOrder: ["clientID", "originClientID", "connectedOnDisplay"], columnNames: { clientID: "Client Name", originClientID: "Origin Name", connectedOnDisplay: "Connected On" }, title: "✨ Online ✨", tableNumber: 1, gradient: { title: ["#00FFFF", "#1E90FF"], header: ["#FFD700", "#FF8C00"], border: ["#2e2f30", "#788e9e"], cell: ["#00BFFF", "#1E90FF"] }, cellColor: (content, column) => { if (column === "clientID") { return chalk.blue(content); } return content; } }); const fileTransferReceiver = new FileTransferReceiverManager(); client.clientTide.onEvent(userID, "current-online-clients", data => { client.clientTide.emitEvent(userID, "check-client-banned", { clientID: userID, tideID: client.clientTide.sockets[userID].tideID }); const currentClients = {}; Object.keys(data).forEach(key => { if (key !== userID) { if (data[key] && data[userID] && data[key].currentTideChannel === data[userID].currentTideChannel) { let currentClient = data[key]; currentClient.connectedOnDisplay = HUDUtilityManager.convertUTCDateToDateDisplay(currentClient.connectedOn, true, 1); currentClients[key] = currentClient; } } }); if (Object.keys(currentClients).length <= 0) { return; } cliTable.setTitle(`✨ Online >${Object.keys(currentClients).length}< ✨`, 1); cliTable.setData(currentClients); console.log(`\n[FileTide ~ FileNet]\n\n`, cliTable.render(), "\n"); if (onCurrentOnlineClients) { onCurrentOnlineClients(currentClients, true); } }); client.clientTide.onEvent(userID, "current-client-tiding", data => { if (onClientTiding) { onClientTiding(data.message, significantMessageColors); } }); client.clientTide.onEvent(userID, "current-clients", data => { const currentClients = {}; Object.keys(data).forEach(key => { if (key !== userID) { currentClients[key] = data[key]; } }); if (Object.keys(currentClients).length <= 0) { return; } _FileTide.outputGradient(`\n[FileTide ~ FileNet]\n\n✨ Current Clients >${Object.keys(currentClients).length}< ✨\n`, significantMessageColors); if (onCurrentOnlineClients) { onCurrentOnlineClients(currentClients, false); } }); client.clientTide.onEvent(userID, "current-client-id-already-online", clientID => { if (userID === clientID) { return; } _FileTide.outputGradient(`[FileTide ~ FileNet] ~ Client ~ ${userID} already online.`, significantMessageColors); if (onCurrentClientIDAlreadyOnline) { onCurrentClientIDAlreadyOnline(clientID); } return; }); client.clientTide.onEvent(userID, "retrieved-client-banned", async bannedDetails => { const clientID = bannedDetails.clientID; if (userID !== clientID) { return; } if (!bannedDetails.banned) { return; } _FileTide.outputGradient(`[FileTide ~ FileNet] ~ Unauthorized access detected.`, errorMessageColors); await this.stopAllMessagers(onBeforeCurrentClientUnauthorizedDisconnect, onCurrentClientUnauthorizedDisconnect, async () => { if (onCurrentClientUnauthorized) { onCurrentClientUnauthorized(bannedDetails); } }); return; }); client.clientTide.onEvent(userID, "save-current-transfer-states", async data => { this.saveTransferStates(); return; }); client.clientTide.onEvent(userID, "current-transfer-state", async data => { const { senderID, fileName, filePath } = data; const currentTransferState = FileTransferRecoveryManager.getTransferState(userID, senderID, `${filePath}-${fileName}`); client.clientTide.emitEvent(userID, "send-current-transfer-state", { recipientID: userID, senderID, transferState: currentTransferState }); return; }); client.clientTide.onEvent(userID, "queue-incoming-transfer", async data => { if (this.transferBarrier && onIncomingTransfer) { onIncomingTransfer(data, status => { client.clientTide.emitEvent(userID, "notify-transfer-barrier", { accepted: status, data }); }); return; } else if (!this.transferBarrier) { client.clientTide.emitEvent(userID, "notify-transfer-barrier", { accepted: true, data }); } return; }); client.clientTide.onEvent(userID, "incoming-transfer", async data => { const { currentSender } = data; if (currentSender && currentSender === userID) { _FileTide.outputGradient(`[${userID}] Transfer successfully sent to ${data.recipientID}`, significantMessageColors); this.progressBarManager.completionMessage = `Successfully sent to ${data.recipientID}!`; this.progressBarManager.displayPage("Page Number:", "Press 'n' for next page, 'p' for previous page.", chalk.blue, chalk.magenta); this.progressBarManager.listenForInput("Page Number:", "Press 'n' for next page, 'p' for previous page.", chalk.blue, chalk.magenta, chalk.yellow); return; } _FileTide.outputGradient(`[${userID}] Preparing to receive transfer : ${FileUtilityManager.getTidePath(data.path)}`, significantMessageColors); if (onTransferStart) { onTransferStart(data); } this.progressBarManager.completionMessage = `Successfully received transfer from ${data.senderID}!`; this.progressBarManager.displayPage("Page Number:", "Press 'n' for next page, 'p' for previous page.", chalk.blue, chalk.magenta); this.progressBarManager.listenForInput("Page Number:", "Press 'n' for next page, 'p' for previous page.", chalk.blue, chalk.magenta, chalk.yellow); }); client.clientTide.onEvent(userID, "incoming-file", async data => { try { _FileTide.outputGradient(`[${userID}] Preparing to receive file : ${data.fileName}`, significantMessageColors); await fileTransferReceiver.initializeTransfer(data); const saveDir = FileUtilityManager.getTidePath(data.path); const verifiedSender = data.senderID && data.senderID !== userID; if (verifiedSender) { this.progressBarManager.addTask({ name: data.fileName, size: data.fileSize }); } if (onIncomingFile) { const taskBarStats = this.progressBarManager.getTaskBar(data.fileName); onIncomingFile(verifiedSender, { ...data, stats: taskBarStats }, FileTransferTrackingManager.activeTransfers); } if (verifiedSender && !fs.existsSync(saveDir)) { fs.mkdirSync(saveDir, { recursive: true }); _FileTide.outputGradient(`[${userID}] Created directory : ${saveDir}`, significantMessageColors); } } catch (error) { _FileTide.outputGradient(`[${userID}] Halted file recieval preparation : ${data.fileName}\n${error}`, errorMessageColors); } }); client.clientTide.onEvent(userID, "accepted-transfer", async data => { _FileTide.outputGradient(`[${userID}] Accepted ${data.clientID}'s transfer!`, significantMessageColors); if (onTransferBarrier) { onTransferBarrier(data); } }); client.clientTide.onEvent(userID, "denied-transfer", async data => { _FileTide.outputGradient(`[${userID}] Denied ${data.clientID}'s transfer.`, errorMessageColors); if (onTransferBarrier) { onTransferBarrier(data); } }); client.clientTide.onEvent(userID, "transfer-progress", async data => { if (!data || data.fileChunk === undefined || data.fileChunk === null) { FileTideDebugUtilityManager.log("notice", true, `Received invalid data chunk from ~ ${data.senderName} in transfer progress ~ ${data.fileName}`); FileTransferTrackingManager.outputCurrentTransfers(); return; } await fileTransferReceiver.handleTransferProgress(data); this.progressBarManager.updateTaskProgress(data.fileName, data.chunkSize || 512 * 1024); const currentTransferState = FileTransferRecoveryManager.getTransferState(userID, data.senderName, `${data.path}-${data.fileName}`) || { transferredSize: 0 }; currentTransferState.transferredSize += data.chunkSize || 512 * 1024; FileTransferRecoveryManager.saveTransferState(userID, data.senderName, `${data.path}-${data.fileName}`, data.chunkIndex, currentTransferState.transferredSize); const taskBarStats = this.progressBarManager.getTaskBar(data.fileName); client.clientTide.emitEvent(userID, "acknowledge-transfer-tide", { fileName: data.fileName, senderName: data.senderName, recipientName: userID, transferStateID: `${data.path}-${data.fileName}`, path: data.path, transferStats: taskBarStats }); if (onTransferProgress) { onTransferProgress({ ...data, stats: taskBarStats }); } }); client.clientTide.onEvent(userID, "transfer-status", data => { const { chunkIndex, chunkSize, fileName, fileSize } = data; if (chunkIndex === 0) { FileTransferTrackingManager.trackTransfer(data); this.progressBarManager.addTask({ name: fileName, size: fileSize }); } else if (chunkIndex > 0) { FileTransferTrackingManager.updateTransfer(data); this.progressBarManager.updateTaskProgress(fileName, chunkSize); } if (onTransferStatus) { const taskBarStats = this.progressBarManager.getTaskBar(fileName); onTransferStatus({ ...data, stats: taskBarStats }); } return; }); client.clientTide.onEvent(userID, "transfer-complete", data => { const savePath = FileUtilityManager.getTidePath(path.join(data.filePath, data.fileName)); try { _FileTide.outputGradient(`[${userID}] File transfer complete ~ ${data.fileName}`, significantMessageColors); const transferState = FileTransferTrackingManager.getTransfer(data.fileName); FileTideDebugUtilityManager.log("notice", true, `Transfer state ~ ${data.fileName} active | ${transferState !== null && transferState !== undefined ? true : false}`); if (!transferState) { return; } if (transferState.fileStream) { fileTransferReceiver.completeTransfer(data, (transferData, transfers, success, error) => { if (success) { FileTransferRecoveryManager.clearTransferState(userID, data.senderID, `${data.filePath}-${data.fileName}`); _FileTide.outputGradient(`[${userID}] File saved at ~ ${savePath}\n`, significantMessageColors); } else if (!success) { _FileTide.outputGradient(`[${userID}] Did not save file at ~ ${savePath}\n\t${error}\n`, errorMessageColors); } if (onTransferComplete) { const taskBarStats = this.progressBarManager.getTaskBar(data.fileName); onTransferComplete({ ...data, stats: taskBarStats }, FileTransferTrackingManager.activeTransfers, success); } setTimeout(() => { FileTransferTrackingManager.outputCurrentTransfers(); this.progressBarManager.clearTaskBar(data.fileName); }, this.progressBarManager.completionTimeout); }); return; } FileTransferRecoveryManager.clearTransferState(userID, data.senderID, `${data.filePath}-${data.fileName}`); FileTransferTrackingManager.untrackTransfer(data.fileName); _FileTide.outputGradient(`[${userID}] Transfer successfully sent to ~ ${data.recipientId} and saved at ~ ${path.join(data.filePath, data.fileName)}\n`, significantMessageColors); if (onTransferComplete) { const taskBarStats = this.progressBarManager.getTaskBar(data.fileName); onTransferComplete({ ...data, stats: taskBarStats }, FileTransferTrackingManager.activeTransfers, true); } setTimeout(() => { FileTransferTrackingManager.outputCurrentTransfers(); this.progressBarManager.clearTaskBar(data.fileName); }, this.progressBarManager.completionTimeout); } catch (error) { _FileTide.outputGradient(`[${userID}] Did not save file at ~ ${savePath}\n${error}`, errorMessageColors); if (onTransferComplete) { const taskBarStats = this.progressBarManager.getTaskBar(data.fileName); onTransferComplete({ ...data, stats: taskBarStats }, FileTransferTrackingManager.activeTransfers, false); } setTimeout(() => { FileTransferTrackingManager.outputCurrentTransfers(); this.progressBarManager.clearTaskBar(data.fileName); }, this.progressBarManager.completionTimeout); } }); client.clientTide.onEvent(userID, "transfer-requested", async data => { _FileTide.outputGradient(`[${userID}] Transfer requested from: ${data.requesterID}`, significantMessageColors); this.clients.get(userID).activeTransferTypes.set(data.destinationPath, data.type); if (onTransferRequested) { onTransferRequested(data); } await this.sendToDevice(data.requesterID, data.path, data.destinationPath, data.filterContent || []); return; }); client.clientTide.onEvent(userID, "list-files", async data => { _FileTide.outputGradient(`[${userID}] Sending file list to ${data.senderID}...!`, significantMessageColors); const currentClientShorePolicies = this.getShorePolicies(userID); let totalFilesSent = 0; const listedFiles = await this.listFiles(currentClientShorePolicies || [">Downloads"], data.depth || 1, (totalItems, directoryContents) => { const capacitor = new ChunkCapacitor(directoryContents, 100); const contents = capacitor.getAllChunks(); for (let files of contents) { totalFilesSent += files.length; client.clientTide.emitEvent(userID, "send-file-list-to-client", { senderID: data.recipientID, recipientID: data.senderID, totalItems: files.length, listedFiles: files, runningOn: this.runningOn }); } }); setTimeout(() => { _FileTide.outputGradient(`[${userID}] Sent file list to ${data.senderID}!`, significantMessageColors); client.clientTide.emitEvent(userID, "sent-file-list-to-client", { senderID: data.recipientID, recipientID: data.senderID, totalItemsSent: totalFilesSent, runningOn: this.runningOn }); }, 1000); }); client.clientTide.onEvent(userID, "error", async error => { _FileTide.outputGradient(`[${userID} | FileTide Error] ${error}`, errorMessageColors); }); client.clientTide.onEvent(userID, "client-file-list-received", async data => { _FileTide.outputGradient(`[${userID}] Successfully received all ${HUDUtilityManager.convertNumberToMoneyFormat(data.totalItemsSent, false)} contents in ${data.senderID}'s file list!`, significantMessageColors); if (onFileListReceived) { onFileListReceived(data); } }); client.clientTide.onEvent(userID, "client-file-list", async data => { client.clientTide.emitEvent(userID, "check-client-banned", { clientID: userID, tideID: client.clientTide.sockets[userID].tideID }); _FileTide.outputGradient(`[${userID}] Received ${HUDUtilityManager.convertNumberToMoneyFormat(data.totalItems, false)} directory contents from ${data.senderID}'s file list!`, significantMessageColors); if (onListFiles) { onListFiles(data); } }); this.currentUserID = userID; return; } ; module.exports = FileTide;