From b78fa8307d5c89088d1c6e8b0229dc2031c3efb9 Mon Sep 17 00:00:00 2001 From: Logan Cusano Date: Sun, 11 Feb 2024 02:45:40 -0500 Subject: [PATCH] Functional join command server side --- server/discordBot/commands/join.mjs | 111 ++++++++++++++++-- .../discordBot/events/interactionCreate.mjs | 8 +- server/modules/socketServerWrappers.mjs | 20 ++-- 3 files changed, 117 insertions(+), 22 deletions(-) diff --git a/server/discordBot/commands/join.mjs b/server/discordBot/commands/join.mjs index 7632a67..a37f61b 100644 --- a/server/discordBot/commands/join.mjs +++ b/server/discordBot/commands/join.mjs @@ -1,23 +1,116 @@ -import { SlashCommandBuilder } from 'discord.js'; +import { SlashCommandBuilder, ActionRowBuilder, ButtonBuilder, ButtonStyle } from 'discord.js'; +import { requestNodeJoinSystem } from '../../modules/socketServerWrappers.mjs'; +import { getSystemsByNuid, getAllSystems, getSystemByName } from '../../modules/mongoSystemsWrappers.mjs'; // Exporting data property export const data = new SlashCommandBuilder() .setName('join') - .setDescription('Replies with your input!'); + .setDescription('Listen to the selected radio system in your channel') + .addStringOption(system => + system.setName('system') + .setDescription('The radio system you would like to listen to') + .setRequired(true) + .setAutocomplete(true)); + +export async function autocomplete(interaction) { + const focusedValue = interaction.options.getFocused(); + const choices = await getAllSystems(); + const filtered = choices.filter(choice => choice.name.startsWith(focusedValue)); + + console.log(focusedValue, choices, filtered); + + await interaction.respond( + filtered.map(choice => ({ name: choice.name, value: choice.name })), + ); +} // Exporting other properties export const example = "/join"; -export const deferInitialReply = false; +export const deferInitialReply = true; // Exporting execute function export async function execute(nodeIo, interaction) { + // Check if the user is in a VC + if (!interaction.member.voice.channel) { return await interaction.reply({ content: 'You need to enter a voice channel before use the command', ephemeral: true }) } + // Grab the channel if the user is connected to VC + const channelToJoin = interaction.member.voice.channel; + + // Get the selected system option from the command interaction + const selectedSystem = interaction.options.getString('system'); + try { - const sockets = await nodeIo.allSockets(); - console.log("All open sockets: ",sockets); - console.log("Open Socket: ", sockets.values(), nodeIo.sockets.sockets.get(sockets[0])) - //await interaction.reply(`**Online Sockets: '${sockets}'**`); - await interaction.reply('**Pong.**'); - //await interaction.channel.send('**Pong.**'); + // Get all open socket nodes + const openSockets = await nodeIo.allSockets(); // TODO - Filter the returned nodes to only nodes that have the radio capability + console.log("All open sockets: ", openSockets); + + // Get the selected system object from the DB + const system = await getSystemByName(selectedSystem); + + // Function wrapper to request the selected/only node to join the selected system + const joinSelectedNode = async (selectedNodeSocketId) => { + const openSocket = await nodeIo.sockets.sockets.get(selectedNodeSocketId); + console.log("Joining selected open socket:", openSocket.id, system.name, channelToJoin.id, openSocket.node.name); + + // Ask the node to join the selected channel and system + await requestNodeJoinSystem(openSocket, system.name, channelToJoin.id); + } + + var availableNodes = []; + // Check each open socket to see if the ID is included in the selected system + await openSockets.forEach(openSocket => { + openSocket = nodeIo.sockets.sockets.get(openSocket); + // TODO - Determine if the node is already connected elsewhere + // Check if this node has the requested system, if so add it to the availble array + if (system.nodes.includes(openSocket.node.nuid)) { + availableNodes.push(openSocket); + } + }); + + console.log("Availble nodes:", availableNodes, availableNodes.map(socket => socket.node.name)); + + // If there are no available nodes, let the user know there are none available + if (availableNodes.length == 0) { + // There are no nodes availble for the requested system + return await interaction.editReply("The selected system has no available nodes"); + } else if (availableNodes.length == 1) { + // There is only one node available for the requested system + // Request the node to join + await joinSelectedNode(availableNodes[0].id); + // Let the user know + await interaction.editReply({ content: `Ok ${interaction.member.id}, a bot will join your channel listening to *'${system.name}'* shortly`, components: [] }); + } else if (availableNodes.length > 1) { + // There is more than one node availble for the requested system + const nodeSelectionButtons = [] + + // Create a button for each available node + for (const availableNode of availableNodes) { + nodeSelectionButtons.push(new ButtonBuilder().setCustomId(availableNode.id).setLabel(availableNode.node.name).setStyle(ButtonStyle.Primary)); + } + + const actionRow = new ActionRowBuilder().addComponents(nodeSelectionButtons); + + // Reply to the user with the button prompts + const response = await interaction.editReply({ + content: "Please select the Node you would like to join with this system", + components: [actionRow] + }); + + // Make sure the responding selection is from the user who initiated the command + const collectorFilter = i => i.user.id === interaction.user.id; + + // Wait for the confirmation from the user on which node to join + try { + const selectedNode = await response.awaitMessageComponent({ filter: collectorFilter, time: 60_000 }); + // Run the local wrapper to listen to the selected node + await joinSelectedNode(selectedNode.customId); + // Let the user know + await selectedNodeConfirmation.update({ content: `Ok ${interaction.member.id}, a bot will join your channel listening to *'${system.name}'*`, components: [] }); + } catch (e) { + console.error(e); + // Timeout the prompt if the user doesn't interact with it + await interaction.editReply({ content: 'Confirmation not received within 1 minute, cancelling', components: [] }); + } + } } catch (err) { console.error(err); // await interaction.reply(err.toString()); diff --git a/server/discordBot/events/interactionCreate.mjs b/server/discordBot/events/interactionCreate.mjs index 64bb668..25332e0 100644 --- a/server/discordBot/events/interactionCreate.mjs +++ b/server/discordBot/events/interactionCreate.mjs @@ -7,12 +7,10 @@ export async function execute(nodeIo, interaction) { console.log("Interaction created for command: ", command); // Execute autocomplete if the user is checking autocomplete - /* if (interaction.isAutocomplete()) { console.log("Running autocomplete for command: ", command.data.name); return await command.autocomplete(interaction); } - */ // Check if the interaction is a command if (!interaction.isChatInputCommand()) return; @@ -24,5 +22,11 @@ export async function execute(nodeIo, interaction) { console.log(`${interaction.member.user} is running '${interaction.commandName}'`); + // Defer the initial reply if the command has the parameter set + if (command.deferInitialReply) { + await interaction.deferReply(); + } + + // Execute the command command.execute(nodeIo, interaction); } \ No newline at end of file diff --git a/server/modules/socketServerWrappers.mjs b/server/modules/socketServerWrappers.mjs index f146723..ef5085f 100644 --- a/server/modules/socketServerWrappers.mjs +++ b/server/modules/socketServerWrappers.mjs @@ -36,7 +36,7 @@ export const nodeLoginWrapper = async (data, socket) => { // Add the socket/node connection socket.node = node; - socket.id = node.nuid; + //socket.id = node.nuid; return; } @@ -151,15 +151,13 @@ const getSocketIdByNuid = async (nuid) => { -const requestNodeJoinPreset = async () => { +export const requestNodeJoinSystem = async (socket, systemName, discordChanelId) => { // Check for System updates - // Test commands - setTimeout(() => { - const joinData = { - 'clientID': "MTE5NjAwNTM2ODYzNjExMjk3Nw.GuCMXg.24iNNofNNumq46FIj68zMe9RmQgugAgfrvelEA", - 'channelID': "367396189529833476", - 'preset': "" - } - sendNodeCommand(socket, "node-join", joinData); - }, 2500) + // Check for open client IDs + const joinData = { + 'clientID': "MTE5NjAwNTM2ODYzNjExMjk3Nw.GuCMXg.24iNNofNNumq46FIj68zMe9RmQgugAgfrvelEA", + 'channelID': discordChanelId, + 'preset': systemName + } + sendNodeCommand(socket, "node-join", joinData); } \ No newline at end of file