From fea7ed2c7f9e0e77d53614a7b5548d58dc4b6565 Mon Sep 17 00:00:00 2001 From: Logan Cusano Date: Sun, 24 Mar 2024 02:45:34 -0400 Subject: [PATCH] #16 - Server can now choose from IDs in the DB - Implemented an active system to disable some IDs from being used --- client/discordAudioBot/dabWrappers.mjs | 25 ++++++- client/modules/socketClient.mjs | 5 +- client/modules/socketClientWrappers.mjs | 11 ++- server/discordBot/commands/join.mjs | 11 ++- server/discordBot/commands/leave.mjs | 19 +---- server/discordBot/modules/wrappers.mjs | 51 +++++++++++++ server/modules/mongoDiscordIDWrappers.mjs | 90 +++++++++++++++++++++++ server/modules/socketServerWrappers.mjs | 19 ++++- 8 files changed, 205 insertions(+), 26 deletions(-) create mode 100644 server/discordBot/modules/wrappers.mjs create mode 100644 server/modules/mongoDiscordIDWrappers.mjs diff --git a/client/discordAudioBot/dabWrappers.mjs b/client/discordAudioBot/dabWrappers.mjs index f5fdf94..3cdcbc6 100644 --- a/client/discordAudioBot/dabWrappers.mjs +++ b/client/discordAudioBot/dabWrappers.mjs @@ -93,11 +93,32 @@ export const checkIfDiscordVCConnected = async (guildId) => { * Get the username of the bot in a given guild * (there may be a server nickname given to the bot in a certain guild) * @param {string} guildId The guild id to check the connection status in - * @returns {string} The username of the bot in the given guild's CV + * @returns {string} The username of the bot in the given guild's VC */ export const getDiscordUsername = async (guildId) => { console.log("Requested username"); - if (activeDiscordClient) return (activeDiscordClient.user.username); + if (activeDiscordClient) { + // Fetch the guild + const guild = await client.guilds.fetch(guildId); + + // Fetch the bot member in the guild + const botMember = await guild.members.fetch(client.user.id); + + // Return bot's nickname if available, otherwise return username + return botMember.nickname || botMember.user.username; + } + else return (undefined); +} + +/** + * Get the ID of the currently running bot + * @returns {string} The ID of the active client + */ +export const getDiscordID = async () => { + console.log("Requested username"); + if (activeDiscordClient) { + return (activeDiscordClient.user.id); + } else return (undefined); } diff --git a/client/modules/socketClient.mjs b/client/modules/socketClient.mjs index d41b766..a25cee2 100644 --- a/client/modules/socketClient.mjs +++ b/client/modules/socketClient.mjs @@ -1,5 +1,5 @@ import { io } from "socket.io-client"; -import { logIntoServerWrapper, nodeCheckStatus, nodeJoinServer, nodeLeaveServer, nodeGetUsername, nodeCheckDiscordClientStatus, nodeCheckCurrentSystem, nodeUpdate } from "./socketClientWrappers.mjs"; +import { logIntoServerWrapper, nodeCheckStatus, nodeJoinServer, nodeLeaveServer, nodeGetUsername, nodeCheckDiscordClientStatus, nodeCheckCurrentSystem, nodeUpdate, nodeGetDiscordID } from "./socketClientWrappers.mjs"; /** * Initialize the socket connection with the server, this will handle disconnects within itself @@ -36,6 +36,9 @@ export const initSocketConnection = async (localNodeConfig) => { // Requested to get the discord username in a given guild socket.on('node-get-discord-username', nodeGetUsername); + // Requested to get the ID of the active discord client + socket.on('node-get-discord-id', nodeGetDiscordID); + // Requested to check if the node is connected to VC in a given guild socket.on('node-check-connected-status', nodeCheckStatus); diff --git a/client/modules/socketClientWrappers.mjs b/client/modules/socketClientWrappers.mjs index 1c01063..32fed46 100644 --- a/client/modules/socketClientWrappers.mjs +++ b/client/modules/socketClientWrappers.mjs @@ -1,4 +1,4 @@ -import { checkIfDiscordVCConnected, joinDiscordVC, leaveDiscordVC, getDiscordUsername, checkIfClientIsOpen } from '../discordAudioBot/dabWrappers.mjs'; +import { checkIfDiscordVCConnected, joinDiscordVC, leaveDiscordVC, getDiscordUsername, checkIfClientIsOpen, getDiscordID } from '../discordAudioBot/dabWrappers.mjs'; import { getCurrentSystem } from '../op25Handler/op25Handler.mjs'; import { checkForUpdates } from './selfUpdater.mjs'; @@ -80,6 +80,15 @@ export const nodeGetUsername = async (guildId, socketCallback) => { socketCallback(await getDiscordUsername(guildId)); } +/** + * Get the ID of the active client + * @param {any} socketCallback The callback function to return the result to + * @callback {any} + */ +export const nodeGetDiscordID = async (socketCallback) => { + socketCallback(await getDiscordID()); +} + /** * Check if the local node has an open discord client in any server diff --git a/server/discordBot/commands/join.mjs b/server/discordBot/commands/join.mjs index 4ba34a5..7ee9078 100644 --- a/server/discordBot/commands/join.mjs +++ b/server/discordBot/commands/join.mjs @@ -1,6 +1,7 @@ import { SlashCommandBuilder, ActionRowBuilder, ButtonBuilder, ButtonStyle } from 'discord.js'; import { requestNodeJoinSystem, checkIfNodeIsConnectedToVC, checkIfNodeHasOpenDiscordClient, getNodeCurrentListeningSystem } from '../../modules/socketServerWrappers.mjs'; import { getSystemsByNuid, getAllSystems, getSystemByName } from '../../modules/mongoSystemsWrappers.mjs'; +import { getAvailableTokensInGuild } from '../modules/wrappers.mjs'; // Exporting data property export const data = new SlashCommandBuilder() @@ -40,7 +41,7 @@ export async function autocomplete(nodeIo, interaction) { */ export async function execute(nodeIo, interaction) { // Check if the user is in a VC - if (!interaction.member.voice.channel) { return await interaction.reply({ content: `<@${interaction.member.id}>, you need to enter a voice channel before use the command`, ephemeral: true }) } + if (!interaction.member.voice.channel) { return await interaction.editReply({ content: `<@${interaction.member.id}>, 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; @@ -54,10 +55,14 @@ export async function execute(nodeIo, interaction) { // 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:", selectedNodeSocketId, system.name, channelToJoin.id, openSocket.node.name); + // Get the open ID for this connection\ + const discordToken = await getAvailableTokensInGuild(nodeIo, interaction.guild.id); + // TODO - Implement a method to have preferred tokens (bot users) for specific systems + + console.log("Joining selected open socket:", selectedNodeSocketId, system.name, channelToJoin.id, openSocket.node.name, discordToken); // Ask the node to join the selected channel and system - await requestNodeJoinSystem(openSocket, system.name, channelToJoin.id); + await requestNodeJoinSystem(openSocket, system.name, channelToJoin.id, discordToken[0].token); } // Get all open socket nodes diff --git a/server/discordBot/commands/leave.mjs b/server/discordBot/commands/leave.mjs index 24f1e77..996fa58 100644 --- a/server/discordBot/commands/leave.mjs +++ b/server/discordBot/commands/leave.mjs @@ -1,5 +1,6 @@ import { SlashCommandBuilder } from 'discord.js'; -import { checkIfNodeIsConnectedToVC, requestBotLeaveServer, getNodeDiscordUsername, getSocketIdByNuid } from '../../modules/socketServerWrappers.mjs'; +import { requestBotLeaveServer, getSocketIdByNuid } from '../../modules/socketServerWrappers.mjs'; +import { checkOnlineBotsInGuild } from '../modules/wrappers.mjs' // Exporting data property export const data = new SlashCommandBuilder() @@ -22,21 +23,7 @@ export const deferInitialReply = true; */ export async function autocomplete(nodeIo, interaction) { const focusedValue = interaction.options.getFocused(); - const choices = []; - - const openSockets = [...await nodeIo.allSockets()]; - await Promise.all(openSockets.map(async openSocket => { - openSocket = await nodeIo.sockets.sockets.get(openSocket); - const connected = await checkIfNodeIsConnectedToVC(nodeIo, interaction.guild.id, openSocket.node.nuid); - console.log("Connected:", connected); - if (connected) { - const username = await getNodeDiscordUsername(openSocket, interaction.guild.id); - choices.push({ - name: username, - value: openSocket.node.nuid - }); - } - })); + const choices = (await checkOnlineBotsInGuild(nodeIo, interaction.guild.id)).map(choice => choice = {name, value: choice.nuid}); const filtered = choices.filter(choice => choice.name.startsWith(focusedValue)); diff --git a/server/discordBot/modules/wrappers.mjs b/server/discordBot/modules/wrappers.mjs new file mode 100644 index 0000000..949306d --- /dev/null +++ b/server/discordBot/modules/wrappers.mjs @@ -0,0 +1,51 @@ +import { checkIfNodeIsConnectedToVC, getNodeDiscordID, getNodeDiscordUsername } from '../../modules/socketServerWrappers.mjs'; +import { getAllDiscordIDs } from '../../modules/mongoDiscordIDWrappers.mjs' + + +export const checkOnlineBotsInGuild = async (nodeIo, guildId) => { + let onlineBots = []; + const openSockets = [...await nodeIo.allSockets()]; + await Promise.all(openSockets.map(async openSocket => { + openSocket = await nodeIo.sockets.sockets.get(openSocket); + const connected = await checkIfNodeIsConnectedToVC(nodeIo, guildId, openSocket.node.nuid); + console.log("Connected:", connected); + if (connected) { + const username = await getNodeDiscordUsername(openSocket, guildId); + const discordID = await getNodeDiscordID(openSocket); + onlineBots.push({ + name: username, + discord_id: discordID, + nuid: openSocket.node.nuid + }); + } + })); + + return onlineBots; + } + + + export const getAvailableTokensInGuild = async (nodeIo, guildId) => { + try { + // Execute both asynchronous functions concurrently + const [discordIDs, onlineBots] = await Promise.all([ + getAllDiscordIDs(), // Fetch all Discord IDs + checkOnlineBotsInGuild(nodeIo, guildId) // Check online bots in the guild + ]); + + // Use the results of both promises here + console.log("Available Discord IDs:", discordIDs); + console.log("Online bots in the guild:", onlineBots); + + // Filter any discordIDs that are not active + var availableDiscordIDs = discordIDs.filter(discordID => discordID.active == true); + + // Filter out discordIDs that are not found in onlineBots + availableDiscordIDs = availableDiscordIDs.filter(discordID => !onlineBots.some(bot => bot.discord_id === discordID.discord_id)); + + // Return the unavailable discordIDs + return availableDiscordIDs; + } catch (error) { + console.error('Error getting available tokens in guild:', error); + throw error; + } +}; \ No newline at end of file diff --git a/server/modules/mongoDiscordIDWrappers.mjs b/server/modules/mongoDiscordIDWrappers.mjs new file mode 100644 index 0000000..1249a62 --- /dev/null +++ b/server/modules/mongoDiscordIDWrappers.mjs @@ -0,0 +1,90 @@ +import { insertDocument, getDocuments, connectToDatabase } from "./mongoHandler.mjs"; + +const collectionName = 'discord-ids'; + +// Wrapper for inserting a Discord ID +export const createDiscordID = async (discordID) => { + try { + const insertedId = await insertDocument(collectionName, discordID); + return insertedId; + } catch (error) { + console.error('Error creating Discord ID:', error); + throw error; + } +}; + +// Wrapper for retrieving all Discord IDs +export const getAllDiscordIDs = async () => { + try { + const discordIDs = await getDocuments(collectionName); + return discordIDs; + } catch (error) { + console.error('Error getting all Discord IDs:', error); + throw error; + } +}; + +// Wrapper for retrieving a Discord ID by name or discord_id +export const getDiscordID = async (identifier) => { + const db = await connectToDatabase(); + try { + const collection = db.db().collection(collectionName); + const discordID = await collection.findOne({ + $or: [ + { name: identifier }, + { discord_id: identifier } + ] + }); + return discordID; + } catch (error) { + console.error('Error getting Discord ID:', error); + throw error; + } finally { + // Close the connection + await db.close(); + } +}; + +// Wrapper for updating a Discord ID by name or discord_id +export const updateDiscordID = async (identifier, updatedFields) => { + const db = await connectToDatabase(); + try { + const collection = db.db().collection(collectionName); + const result = await collection.updateOne({ + $or: [ + { name: identifier }, + { discord_id: identifier } + ] + }, { $set: updatedFields }); + console.log('Discord ID updated:', result.modifiedCount); + return result.modifiedCount; + } catch (error) { + console.error('Error updating Discord ID:', error); + throw error; + } finally { + // Close the connection + await db.close(); + } +}; + +// Wrapper for deleting a Discord ID by name or discord_id +export const deleteDiscordID = async (identifier) => { + const db = await connectToDatabase(); + try { + const collection = db.db().collection(collectionName); + const result = await collection.deleteOne({ + $or: [ + { name: identifier }, + { discord_id: identifier } + ] + }); + console.log('Discord ID deleted:', result.deletedCount); + return result.deletedCount; + } catch (error) { + console.error('Error deleting Discord ID:', error); + throw error; + } finally { + // Close the connection + await db.close(); + } +}; \ No newline at end of file diff --git a/server/modules/socketServerWrappers.mjs b/server/modules/socketServerWrappers.mjs index 30efd76..5fac66e 100644 --- a/server/modules/socketServerWrappers.mjs +++ b/server/modules/socketServerWrappers.mjs @@ -257,16 +257,29 @@ export const getNodeDiscordUsername = async (socket, guildId) => { }); } +/** + * 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) => { - // Check for open client IDs +export const requestNodeJoinSystem = async (socket, systemName, discordChanelId, discordToken = "MTE5NjAwNTM2ODYzNjExMjk3Nw.GuCMXg.24iNNofNNumq46FIj68zMe9RmQgugAgfrvelEA") => { + // Join the system const joinData = { - 'clientID': "MTE5NjAwNTM2ODYzNjExMjk3Nw.GuCMXg.24iNNofNNumq46FIj68zMe9RmQgugAgfrvelEA", + 'clientID': discordToken, 'channelID': discordChanelId, 'system': systemName }