Functional joining and leaving
- Needs to be tested on multiple servers - Needs to be tested with multiple nodes #1 #9
This commit is contained in:
@@ -97,6 +97,10 @@ export async function checkIfConnectedToVC(guildId) {
|
||||
return connection
|
||||
}
|
||||
|
||||
export const getVoiceConnectionFromGuild = async (guildId) => {
|
||||
return getVoiceConnection(guildId);
|
||||
}
|
||||
|
||||
export async function initDiscordBotClient(token, systemName, readyCallback) {
|
||||
const client = new Client({
|
||||
intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMessages, GatewayIntentBits.GuildVoiceStates, GatewayIntentBits.MessageContent],
|
||||
@@ -104,10 +108,16 @@ export async function initDiscordBotClient(token, systemName, readyCallback) {
|
||||
|
||||
client.on(Events.ClientReady, () => {
|
||||
console.log('discord.js client is ready!');
|
||||
|
||||
// Attach the recorder to the VC connection
|
||||
attachRecorder();
|
||||
|
||||
// Set the activity of the bot user
|
||||
client.user.setPresence({
|
||||
activities: [{ name: `${systemName}`, type: ActivityType.Listening }],
|
||||
});
|
||||
|
||||
//
|
||||
readyCallback(client);
|
||||
});
|
||||
|
||||
|
||||
@@ -1,49 +1,61 @@
|
||||
import { io } from "socket.io-client";
|
||||
import { connectToChannel, initDiscordBotClient, getVoiceChannelFromID, checkIfConnectedToVC } from '../discordAudioBot/dab.mjs';
|
||||
import { logIntoServerWrapper, sendNodeUpdateWrapper } from "./socketClientWrappers.mjs";
|
||||
import { connectToChannel, initDiscordBotClient, getVoiceChannelFromID, checkIfConnectedToVC, getVoiceConnectionFromGuild } from '../discordAudioBot/dab.mjs';
|
||||
import { logIntoServerWrapper, sendNodeUpdateWrapper, nodeCheckStatus } from "./socketClientWrappers.mjs";
|
||||
|
||||
/**
|
||||
* Initialize the socket connection with the server, this will handle disconnects within itself
|
||||
* @param {Object} localNodeConfig The local node config object
|
||||
* @returns {any}
|
||||
*/
|
||||
export const initSocketConnection = async (localNodeConfig) => {
|
||||
const serverEndpoint = `http://${localNodeConfig.serverIp}:${localNodeConfig.serverPort}` || 'http://localhost:3000'; // Adjust the server endpoint
|
||||
|
||||
const socket = io.connect(serverEndpoint);
|
||||
|
||||
const discordClients = {};
|
||||
|
||||
socket.on('connect', async () => {
|
||||
console.log('Connected to the server');
|
||||
await logIntoServerWrapper(socket, localNodeConfig);
|
||||
});
|
||||
|
||||
socket.on('node-join', async (joinData) => {
|
||||
socket.on('node-join', async (joinData) => {
|
||||
console.log("Join requested: ", joinData)
|
||||
// TODO - Implement logic to control OP25 for the requested channel/system
|
||||
|
||||
// Join the requested channel with the requested ID
|
||||
initDiscordBotClient(joinData.clientID, joinData.system, client => {
|
||||
getVoiceChannelFromID(client, joinData.channelID).then(vc => {
|
||||
// Add the client object to the IO instance
|
||||
discordClients[vc.guild.id] = client;
|
||||
const connection = connectToChannel(vc);
|
||||
console.log("Bot Connected to VC");
|
||||
})
|
||||
});
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
socket.on('node-leave', async () => {
|
||||
socket.on('node-leave', async (guildId) => {
|
||||
console.log("Leave requested");
|
||||
const connection = await getVoiceConnection(myVoiceChannel.guild.id);
|
||||
if (connection) {
|
||||
console.log("There is an open VC connection, closing it now");
|
||||
connection.destroy();
|
||||
if (await checkIfConnectedToVC(guildId)) {
|
||||
const connection = await getVoiceConnectionFromGuild(guildId);
|
||||
if (connection) {
|
||||
console.log("There is an open VC connection, closing it now");
|
||||
// Destroy the open VC connection
|
||||
connection.destroy();
|
||||
|
||||
// Remove the client from the socket connection
|
||||
delete discordClients[guildId];
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('node-check-connected-status', async (guildId, socketCallback) => {
|
||||
console.log("Requested status check");
|
||||
if (await checkIfConnectedToVC(guildId)) {
|
||||
console.log("There is an open VC connection");
|
||||
socketCallback(true);
|
||||
} else {
|
||||
socketCallback(false);
|
||||
}
|
||||
socket.on('node-get-discord-username', async (guildId, socketCallback) => {
|
||||
console.log("Requested username");
|
||||
socketCallback(discordClients[guildId].user.username);
|
||||
});
|
||||
|
||||
socket.on('node-check-connected-status', nodeCheckStatus);
|
||||
|
||||
socket.on('disconnect', () => {
|
||||
console.log('Disconnected from the server');
|
||||
});
|
||||
|
||||
@@ -1,11 +1,60 @@
|
||||
import { checkIfConnectedToVC } from '../discordAudioBot/dab.mjs';
|
||||
|
||||
/**
|
||||
* Wrapper to log into the server
|
||||
* @param {any} socket The socket connection with the server
|
||||
* @param {object} localNodeConfig The local node object
|
||||
* @returns {any}
|
||||
*/
|
||||
export const logIntoServerWrapper = async (socket, localNodeConfig) => {
|
||||
// Log into the server
|
||||
socket.emit("node-login", localNodeConfig.node);
|
||||
|
||||
// Send an update to the server
|
||||
sendNodeUpdateWrapper(socket, localNodeConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the server an update
|
||||
* @param {any} socket The socket connection with the server
|
||||
* @param {object} localNodeConfig The local node object
|
||||
*/
|
||||
export const sendNodeUpdateWrapper = async (socket, localNodeConfig) => {
|
||||
socket.emit('node-update', {
|
||||
'node': localNodeConfig.node,
|
||||
'nearbySystems': localNodeConfig.nearbySystems
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
export const nodeJoinServer = async (joinData) => {
|
||||
console.log("Join requested: ", joinData)
|
||||
// TODO - Implement logic to control OP25 for the requested channel/system
|
||||
|
||||
// Join the requested channel with the requested ID
|
||||
initDiscordBotClient(joinData.clientID, joinData.system, client => {
|
||||
getVoiceChannelFromID(client, joinData.channelID).then(vc => {
|
||||
// Add the client object to the IO instance
|
||||
discordClients[vc.guild.id] = client;
|
||||
const connection = connectToChannel(vc);
|
||||
console.log("Bot Connected to VC");
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if the bot is connected to a discord VC in the given server
|
||||
* @param {string} guildId The guild id to check the connection status in
|
||||
* @param {any} socketCallback The callback function to return the result to
|
||||
* @returns {any}
|
||||
*/
|
||||
export const nodeCheckStatus = async (guildId, socketCallback) => {
|
||||
console.log("Requested status check");
|
||||
if (await checkIfConnectedToVC(guildId)) {
|
||||
console.log("There is an open VC connection");
|
||||
socketCallback(true);
|
||||
} else {
|
||||
socketCallback(false);
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,16 @@ export const data = new SlashCommandBuilder()
|
||||
.setRequired(true)
|
||||
.setAutocomplete(true));
|
||||
|
||||
export async function autocomplete(interaction) {
|
||||
// Exporting other properties
|
||||
export const example = "/join";
|
||||
export const deferInitialReply = true;
|
||||
|
||||
/**
|
||||
* Function to give the user auto-reply suggestions
|
||||
* @param {any} nodeIo The nodeIO server for manipulation of sockets
|
||||
* @param {any} interaction The interaction object
|
||||
*/
|
||||
export async function autocomplete(nodeIo, interaction) {
|
||||
const focusedValue = interaction.options.getFocused();
|
||||
const choices = await getAllSystems();
|
||||
const filtered = choices.filter(choice => choice.name.startsWith(focusedValue));
|
||||
@@ -24,14 +33,14 @@ export async function autocomplete(interaction) {
|
||||
);
|
||||
}
|
||||
|
||||
// Exporting other properties
|
||||
export const example = "/join";
|
||||
export const deferInitialReply = true;
|
||||
|
||||
// Exporting execute function
|
||||
/**
|
||||
* The function to run when the command is called by a discord user
|
||||
* @param {any} nodeIo The nodeIO server for manipulation of sockets
|
||||
* @param {any} interaction The interaction object
|
||||
*/
|
||||
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 }) }
|
||||
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 }) }
|
||||
// Grab the channel if the user is connected to VC
|
||||
const channelToJoin = interaction.member.voice.channel;
|
||||
|
||||
@@ -74,7 +83,7 @@ export async function execute(nodeIo, interaction) {
|
||||
// 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");
|
||||
return await interaction.editReply(`<@${interaction.member.id}>, 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
|
||||
@@ -94,7 +103,7 @@ export async function execute(nodeIo, interaction) {
|
||||
|
||||
// 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",
|
||||
content: `<@${interaction.member.id}>, Please select the Node you would like to join with this system`,
|
||||
components: [actionRow]
|
||||
});
|
||||
|
||||
|
||||
67
server/discordBot/commands/leave.mjs
Normal file
67
server/discordBot/commands/leave.mjs
Normal file
@@ -0,0 +1,67 @@
|
||||
import { SlashCommandBuilder } from 'discord.js';
|
||||
import { checkIfNodeIsConnectedToVC, requestBotLeaveServer, getNodeDiscordUsername, getSocketIdByNuid } from '../../modules/socketServerWrappers.mjs';
|
||||
|
||||
// Exporting data property
|
||||
export const data = new SlashCommandBuilder()
|
||||
.setName('leave')
|
||||
.setDescription('Disconnect a bot from the server')
|
||||
.addStringOption(system =>
|
||||
system.setName('bot')
|
||||
.setDescription('The bot you would like to disconnect')
|
||||
.setRequired(true)
|
||||
.setAutocomplete(true));;
|
||||
|
||||
// Exporting other properties
|
||||
export const example = "/leave *{Bot Name}*";
|
||||
export const deferInitialReply = true;
|
||||
|
||||
/**
|
||||
* Function to give the user auto-reply suggestions
|
||||
* @param {any} nodeIo The nodeIO server for manipulation of sockets
|
||||
* @param {any} interaction The interaction object
|
||||
*/
|
||||
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 filtered = choices.filter(choice => choice.name.startsWith(focusedValue));
|
||||
|
||||
console.log(focusedValue, choices, filtered);
|
||||
|
||||
await interaction.respond(filtered);
|
||||
}
|
||||
|
||||
/**
|
||||
* The function to run when the command is called by a discord user
|
||||
* @param {any} nodeIo The nodeIO server for manipulation of sockets
|
||||
* @param {any} interaction The interaction object
|
||||
*/
|
||||
export async function execute(nodeIo, interaction) {
|
||||
try {
|
||||
// Get the requested bot
|
||||
const selectedNode = interaction.options.getString('bot');
|
||||
const socket = await getSocketIdByNuid(nodeIo, selectedNode);
|
||||
console.log("All open sockets:", socket, selectedNode);
|
||||
await requestBotLeaveServer(socket, interaction.guild.id);
|
||||
//await interaction.reply(`**Online Sockets: '${sockets}'**`);
|
||||
await interaction.editReply(`Ok <@${interaction.member.id}>, the bot is leaving shortly`);
|
||||
//await interaction.channel.send('**Pong.**');
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
// await interaction.reply(err.toString());
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@ export async function execute(nodeIo, interaction) {
|
||||
// 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);
|
||||
return await command.autocomplete(nodeIo, interaction);
|
||||
}
|
||||
|
||||
// Check if the interaction is a command
|
||||
|
||||
@@ -66,7 +66,6 @@ export const nodeUpdateWrapper = async (nodeData) => {
|
||||
* 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
|
||||
* @returns {any}
|
||||
*/
|
||||
export const nearbySystemsUpdateWraper = async (nuid, nearbySystems) => {
|
||||
console.log("System updates sent by node: ", nuid, nearbySystems);
|
||||
@@ -142,9 +141,12 @@ export const nearbySystemsUpdateWraper = async (nuid, nearbySystems) => {
|
||||
* @param {string} nuid The NUID to find within the open sockets
|
||||
* @returns {string|null} Will return the open socket ID or NULL
|
||||
*/
|
||||
const getSocketIdByNuid = async (nodeIo, nuid) => {
|
||||
for (const openSocket in await nodeIo.allSockets()) {
|
||||
if (openSockets[openSocket] == nuid)
|
||||
export const getSocketIdByNuid = async (nodeIo, nuid) => {
|
||||
const openSockets = await nodeIo.allSockets();
|
||||
for (const openSocketId of openSockets) {
|
||||
console.log(openSockets)
|
||||
const openSocket = await nodeIo.sockets.sockets.get(openSocketId);
|
||||
if (openSocket.node.nuid == nuid)
|
||||
return openSocket;
|
||||
}
|
||||
return null;
|
||||
@@ -152,9 +154,9 @@ const getSocketIdByNuid = async (nodeIo, nuid) => {
|
||||
|
||||
/**
|
||||
* Get all nodes that are connected to a voice channel
|
||||
* @param {any} nodeIo
|
||||
* @param {any} guildId The guild ID string for the guild we are looking in
|
||||
* @returns {any}
|
||||
* @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
|
||||
@@ -167,7 +169,7 @@ export const getAllSocketsConnectedToVC = async (nodeIo, guildId) => {
|
||||
await new Promise((res) => {
|
||||
openSocket.emit('node-check-connected-status', guildId, (status) => {
|
||||
if (status) {
|
||||
console.log("Socket is connected to VC:", openSocket.node.name);
|
||||
console.log("Socket is connected to VC:", openSocket.node.name, status);
|
||||
socketsConnectedToVC.push(openSocket);
|
||||
} else {
|
||||
console.log("Socket is NOT connected to VC:", openSocket.node.name);
|
||||
@@ -183,8 +185,8 @@ export const getAllSocketsConnectedToVC = async (nodeIo, guildId) => {
|
||||
/**
|
||||
* Wrapper to check if the given NUID is connected to a VC
|
||||
* @param {any} nodeIo The nodeIo object that contains the IO server
|
||||
* @param {any} nuid The NUID string that we would like to find in the open socket connections
|
||||
* @returns {any}
|
||||
* @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);
|
||||
@@ -196,12 +198,25 @@ export const checkIfNodeIsConnectedToVC = async (nodeIo, guildId, nuid) => {
|
||||
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);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 {any} discordChanelId The Discord channel ID to join the listening bot to
|
||||
* @returns {any}
|
||||
* @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
|
||||
@@ -214,6 +229,12 @@ export const requestNodeJoinSystem = async (socket, systemName, discordChanelId)
|
||||
await sendNodeCommand(socket, "node-join", joinData);
|
||||
}
|
||||
|
||||
export const requestBotLeave = async () => {
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
Reference in New Issue
Block a user