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); } }); }