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