// Modules const { SlashCommandBuilder } = require('discord.js'); const { DebugBuilder } = require("../utilities/debugBuilder"); const { getMembersInRole, getAllClientIds, filterAutocompleteValues } = require("../utilities/utils"); const { requestOptions, sendHttpRequest } = require("../utilities/httpRequests"); const { getOnlineNodes, updateNodeInfo, addNodeConnection, getConnectionByNodeId, getAllConnections } = require("../utilities/mysqlHandler"); // Global Vars const log = new DebugBuilder("server", "join"); /** * * This wrapper will check if there is an available node with the requested preset and if so checks for an available client ID to join with * * @param {*} presetName The preset name to listen to on the client * @param {*} channelId The channel ID to join the bot to * @param {*} connections EITHER A collection of clients that are currently connected OR a single discord client ID (NOT dev portal ID) that should be used to join the server with * @returns */ async function joinServerWrapper(presetName, channelId, connections) { // Get nodes online var onlineNodes = await new Promise((recordResolve, recordReject) => { getOnlineNodes((nodeRows) => { recordResolve(nodeRows); }); }); // Check which nodes have the selected preset onlineNodes = onlineNodes.filter(node => node.nearbySystems.includes(presetName)); log.DEBUG("Filtered Online Nodes: ", onlineNodes); // Check if any nodes with this preset are available var nodesCurrentlyAvailable = []; for (const node of onlineNodes) { const currentConnection = await getConnectionByNodeId(node.id); log.DEBUG("Checking to see if there is a connection for Node: ", node, currentConnection); if(!currentConnection) nodesCurrentlyAvailable.push(node); } log.DEBUG("Nodes Currently Available: ", nodesCurrentlyAvailable); // If not, let the user know if (!nodesCurrentlyAvailable.length > 0) return Error("All nodes with this channel are unavailable, consider swapping one of the currently joined bots."); // If so, join with the first node var availableClientIds = await getAllClientIds(); log.DEBUG("All clients: ", Object.keys(availableClientIds)); var selectedClientId; if (typeof connections === 'string') { for (const availableClientId of availableClientIds) { if (availableClientId.discordId != connections ) selectedClientId = availableClientId; } } else { log.DEBUG("Open connections: ", connections); for (const connection of connections) { log.DEBUG("Used Client ID: ", connection); availableClientIds = availableClientIds.filter(cid => cid.discordId != connection.clientObject.discordId); } log.DEBUG("Available Client IDs: ", availableClientIds); if (!Object.keys(availableClientIds).length > 0) return log.ERROR("All client ID have been used, consider swapping one of the curretly joined bots or adding more Client IDs to the pool.") selectedClientId = availableClientIds[0]; } const selectedNode = nodesCurrentlyAvailable[0]; const reqOptions = new requestOptions("/bot/join", "POST", selectedNode.ip, selectedNode.port); const postObject = { "channelId": channelId, "clientId": selectedClientId.clientId, "presetName": presetName }; log.INFO("Post Object: ", postObject); sendHttpRequest(reqOptions, JSON.stringify(postObject), async (responseObj) => { log.VERBOSE("Response Object from node ", selectedNode, responseObj); if (!responseObj || !responseObj.statusCode == 200) return false; // Node has connected to discord // Updating node Object in DB const updatedNode = await updateNodeInfo(selectedNode); log.DEBUG("Updated Node: ", updatedNode); // Adding a new node connection const nodeConnection = await addNodeConnection(selectedNode, selectedClientId); log.DEBUG("Node Connection: ", nodeConnection); }); return selectedClientId; } exports.joinServerWrapper = joinServerWrapper; module.exports = { data: new SlashCommandBuilder() .setName('join') .setDescription('Join the channel you are in with the preset you choose') .addStringOption(option => option.setName("preset") .setDescription("The preset you would like to listen to") .setAutocomplete(true) .setRequired(true)), example: "join", isPrivileged: false, requiresTokens: false, defaultTokenUsage: 0, deferInitialReply: true, async autocomplete(interaction) { const nodeObjects = await new Promise((recordResolve, recordReject) => { getOnlineNodes((nodeRows) => { recordResolve(nodeRows); }); }); log.DEBUG("Node objects: ", nodeObjects); var presetsAvailable = []; for (const nodeObject of nodeObjects) { log.DEBUG("Node object: ", nodeObject); for (const presetName in nodeObject.nearbySystems) presetsAvailable.push(nodeObject.nearbySystems[presetName]); } log.DEBUG("All Presets available: ", presetsAvailable); // Remove duplicates options = [...new Set(presetsAvailable)]; log.DEBUG("DeDuped Presets available: ", options); // Filter the results to what the user is entering filterAutocompleteValues(interaction, options); }, async execute(interaction) { try{ const guildId = interaction.guild.id; const presetName = interaction.options.getString('preset'); if (!interaction.member.voice.channel.id) return interaction.editReply(`You need to be in a voice channel, ${interaction.user}`) const channelId = interaction.member.voice.channel.id; log.DEBUG(`Join requested by: ${interaction.user.username}, to: '${presetName}', in channel: ${channelId} / ${guildId}`); const connections = await getAllConnections(); log.DEBUG("Current Connections: ", connections); const selectedClientId = await joinServerWrapper(presetName, channelId, connections); await interaction.editReply(`Ok, ${interaction.member}. **${selectedClientId.name}** is joining your channel.`); //await interaction.channel.send('**Pong.**'); // This will send a message to the channel of the interaction outside of the initial reply }catch(err){ log.ERROR(err) //await interaction.reply(err.toString()); } } }