import { DebugBuilder } from "../modules/debugger.mjs"; const log = new DebugBuilder("server", "socketServerWrappers"); import { createNode, getNodeByNuid, updateNodeByNuid, } from "./mongo-wrappers/mongoNodesWrappers.mjs"; import { createSystem, getSystemByName, updateSystemByName, getSystemsByNuid, deleteSystemByName, } from "./mongo-wrappers/mongoSystemsWrappers.mjs"; /** * Description * @param {any} socket * @param {any} command * @param {any} data * @returns {any} */ const sendNodeCommand = async (socket, command, data) => { // TODO - Check to see if the command exists // TODO - Check to see if the socket is alive? // TODO - Validate the given data socket.emit(command, data); }; /** * Log the node into the network * @param {object} data The data sent from the node * @param {any} socket The socket the node is connected from * @returns {any} */ export const nodeLoginWrapper = async (data, socket) => { log.INFO(`Login requested from node: ${data.nuid}`, data); // Check to see if node exists var node = await getNodeByNuid(data.nuid); if (!node) { const insertedId = await createNode(data); log.DEBUG("Added new node to the database:", insertedId); } else { // Check for updates const updatedNode = await updateNodeByNuid(data.nuid, data); log.DEBUG("Updated node:", updatedNode); } node = await getNodeByNuid(data.nuid); // Add the socket/node connection socket.node = node; return; }; /** * Disconnect the client from the server * @param {string} socketId The socket ID that was disconnected * @returns {any} */ export const nodeDisconnectWrapper = async (socketId) => { // TODO - Let any server know that a bot has disconnected if the bot was joined to vc? might not be worth cpu lol return; }; /** * Update node data in the database * @param {object} nodeData The data object sent from the node * @returns {any} */ export const nodeUpdateWrapper = async (nodeData) => { log.DEBUG("Data update sent by node: ", nodeData); const updateResults = await updateNodeByNuid(nodeData.nuid, nodeData); return; }; /** * Wrapper to update the systems from the nearbySystems object passed from clients * @param {string} nuid The NUID of the node that sent the update * @param {object} nearbySystems The nearby systems object passed from the node to be updated */ export const nearbySystemsUpdateWraper = async (nuid, nearbySystems) => { log.DEBUG("System updates sent by node: ", nuid, nearbySystems); // Check to see if the node removed any systems const existingSystems = await getSystemsByNuid(nuid); log.DEBUG("Existing systems:", existingSystems); if (existingSystems !== nearbySystems) { for (const existingSystem of existingSystems) { if (existingSystem.name in nearbySystems) { // Skip this system if it's in the given systems update continue; } log.DEBUG("System exists that was not given by node", existingSystem); // Check if this node was the only node on this system if (existingSystem.nodes.filter((node) => node !== nuid).length === 0) { // Remove the system if so log.INFO( "Given node was the only node on this system, removing the system...", ); await deleteSystemByName(existingSystem.name); } else { // Remove the node from the array if there are other nodes with this system log.INFO("Other nodes found on this system, removing the given NUID"); existingSystem.nodes = existingSystem.nodes.filter( (node) => node !== nuid, ); log.DEBUG(existingSystem); await updateSystemByName(existingSystem.name, existingSystem); } } } // Add and update the given systems for (const nearbySystem in nearbySystems) { // Check if the system exists already on another node const existingSystem = await getSystemByName(nearbySystem); if (existingSystem) { // Verify the frequencies match (to make sure the name isn't just the same) if ( JSON.stringify(existingSystem.frequencies) === JSON.stringify(nearbySystems[nearbySystem].frequencies) ) { // The systems are the same // Check if the current node is listed in the nodes, if not add it if (!existingSystem.nodes.includes(nuid)) { existingSystem.nodes.push(nuid); // Update the system with the added node const updateResults = await updateSystemByName( nearbySystem, existingSystem, ); if (updateResults) log.INFO("System updated", nearbySystem); } } else { // The systems are not the same // TODO - Implement logic to handle if system names match, but they are for different frequencies or have additional freqs // Check if the current node is listed in the nodes, if not add it if (!existingSystem.nodes.includes(nuid)) { existingSystem.nodes.push(nuid); nearbySystems[nearbySystem].nodes = existingSystem.nodes; } // Update the system with the added node const updateResults = await updateSystemByName( nearbySystem, nearbySystems[nearbySystem], ); if (updateResults) log.INFO("System updated", nearbySystem); } } else { // Create a new system const newSystem = await createSystem( nearbySystem, nearbySystems[nearbySystem], nuid, ); log.INFO("New system created", nearbySystem, newSystem); } } return; }; /** * Get the open socket connection ID for a node from the NUID * @param {string} nuid The NUID to find within the open sockets * @returns {string|null} Will return the open socket ID or NULL */ export const getSocketIdByNuid = async (nodeIo, nuid) => { const openSockets = await nodeIo.allSockets(); for (const openSocketId of openSockets) { log.DEBUG(openSockets); const openSocket = await nodeIo.sockets.sockets.get(openSocketId); if (openSocket.node.nuid == nuid) return openSocket; } return null; }; /** * Get all nodes that are connected to a voice channel * @param {any} nodeIo The nodeIo object that contains the IO server * @param {string} guildId The guild ID string for the guild we are looking in * @returns {Array} The sockets connected to VC in a given server */ export const getAllSocketsConnectedToVC = async (nodeIo, guildId) => { // Get all open socket nodes // TODO - require a server guild to filter the results, ie this would be able to check what server the VCs the nodes are connected are in const openSockets = [...(await nodeIo.allSockets())]; // TODO - Filter the returned nodes to only nodes that have the radio capability // Check each open socket to see if the node has the requested system const socketsConnectedToVC = []; await Promise.all( openSockets.map(async (openSocket) => { openSocket = await nodeIo.sockets.sockets.get(openSocket); await new Promise((res) => { openSocket.emit("node-check-connected-status", guildId, (status) => { if (status) { log.INFO( "Socket is connected to VC:", openSocket.node.name, status, ); socketsConnectedToVC.push(openSocket); } else { log.INFO("Socket is NOT connected to VC:", openSocket.node.name); } res(); }); }); }), ); return socketsConnectedToVC; }; /** * Check if the given node has an open discord client * @param {any} openSocket The open socket connection with the node to check * @returns {boolean} If the given node has an open discord client or not */ export const checkIfNodeHasOpenDiscordClient = async (openSocket) => { // Check the open socket to see if the node has an open discord client let hasOpenDiscordClient = false; await new Promise((res) => { log.INFO( "Checking if socket has an open connection:", openSocket.node.name, ); openSocket.emit("node-check-discord-open-client", (status) => { if (status) { log.INFO( "Socket has an open discord client:", openSocket.node.name, status, ); hasOpenDiscordClient = true; } else { log.INFO( "Socket does NOT have an open discord client:", openSocket.node.name, ); } res(); }); }); return hasOpenDiscordClient; }; export const getNodeCurrentListeningSystem = async (openSocket) => { const hasOpenClient = checkIfNodeHasOpenDiscordClient(openSocket); if (!hasOpenClient) return undefined; // check what system the socket is listening to let currentSystem = undefined; await new Promise((res) => { log.INFO( "Checking system node is currently listening to:", openSocket.node.name, ); openSocket.emit("node-check-current-system", (system) => { if (system) { log.INFO( "Socket is listening to system:", openSocket.node.name, system, ); currentSystem = system; } else { log.INFO( "Socket is not currently listening to a system:", openSocket.node.name, ); } res(); }); }); return currentSystem; }; /** * Wrapper to check if the given NUID is connected to a VC * @param {any} nodeIo The nodeIo object that contains the IO server * @param {string} nuid The NUID string that we would like to find in the open socket connections * @returns {boolean} If the node is connected to VC in the given server */ export const checkIfNodeIsConnectedToVC = async (nodeIo, guildId, nuid) => { const socketsConnectedToVC = await getAllSocketsConnectedToVC( nodeIo, guildId, ); for (const socket of socketsConnectedToVC) { if (socket.node.nuid === nuid) { return true; } } return false; }; /** * Get the discord username from a given socket * @param {any} socket The socket object of the node to check the username of * * @param {string} guildId The guild ID to check the username in * @returns {string} The username of the bot in the requested server */ export const getNodeDiscordUsername = async (socket, guildId) => { return await new Promise((res) => { socket.emit("node-get-discord-username", guildId, (username) => { res(username); }); }); }; /** * Get the discord ID from a given socket * @param {any} socket The socket object of the node to check the ID of * @returns {string} The ID of the bot */ export const getNodeDiscordID = async (socket) => { return await new Promise((res) => { socket.emit("node-get-discord-id", (discordID) => { res(discordID); }); }); }; /** * Request a given socket node to join a given voice channel * @param {any} socket The socket object of the node the request should be sent to * @param {any} systemName The system preset name that we would like to listen to * @param {string} discordChanelId The Discord channel ID to join the listening bot to */ export const requestNodeJoinSystem = async ( socket, systemName, discordChanelId, discordToken = "MTE5NjAwNTM2ODYzNjExMjk3Nw.GuCMXg.24iNNofNNumq46FIj68zMe9RmQgugAgfrvelEA", ) => { // Join the system const joinData = { clientID: discordToken, channelID: discordChanelId, system: systemName, }; // Send the command to the node await sendNodeCommand(socket, "node-join", joinData); }; /** * Request a given socket node to leave VC in a given server * @param {any} socket The socket object of the node the request should be sent to * @param {string} guildId The guild ID to disconnect the socket node from */ export const requestBotLeaveServer = async (socket, guildId) => { // Send the command to the node await sendNodeCommand(socket, "node-leave", guildId); }; /** * Requset a given socket node to update themselves * @param {any} socket The socket object of the node to request to update */ export const requestNodeUpdate = async (socket) => { await sendNodeCommand(socket, "node-update", (status) => { if (status) { log.INFO("Node is out of date, updating now", socket.node.name); } else { log.INFO("Node is up to date", socket.node.name); } }); };