Initial removal of internal discord bot
This commit is contained in:
@@ -1,88 +0,0 @@
|
||||
// Config
|
||||
const { getDeviceID } = require('../utilities/configHandler.js');
|
||||
// Modules
|
||||
const portAudio = require('naudiodon');
|
||||
const { returnAlsaDeviceObject } = require("../utilities/executeConsoleCommands.js");
|
||||
// Debug
|
||||
const { DebugBuilder } = require("../utilities/debugBuilder.js");
|
||||
// Global Vars
|
||||
const log = new DebugBuilder("client", "audioController");
|
||||
|
||||
/**
|
||||
* Checks to make sure the selected audio device is available and returns the device object (PortAudio Device Info)
|
||||
* At least one option must be supplied, it will prefer ID to device name
|
||||
*
|
||||
* @param deviceName The name of the device being queried
|
||||
* @param deviceId The ID of the device being queried
|
||||
* @returns {unknown}
|
||||
*/
|
||||
async function confirmAudioDevice({deviceName = undefined, deviceId = undefined}){
|
||||
const deviceList = await getAudioDevices();
|
||||
if (!deviceName && !deviceId) throw new Error("No device given");
|
||||
let confirmedDevice;
|
||||
if (deviceId) confirmedDevice = deviceList.find(device => device.id === deviceId);
|
||||
if (deviceName) confirmedDevice = deviceList.find(device => device.name === deviceName);
|
||||
|
||||
log.DEBUG("Confirmed Audio Device: ", confirmedDevice);
|
||||
|
||||
return confirmedDevice;
|
||||
}
|
||||
exports.confirmAudioDevice = confirmAudioDevice;
|
||||
|
||||
/**
|
||||
* Return a list of the audio devices connected with input channels
|
||||
*
|
||||
* @returns {unknown[]}
|
||||
*/
|
||||
async function getAudioDevices(){
|
||||
// Exec output contains both stderr and stdout outputs
|
||||
//const deviceList = await returnAlsaDeviceObject();
|
||||
const deviceList = portAudio.getDevices().map((device) => {
|
||||
if (device.maxInputChannels > 2) {
|
||||
return device;
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}).filter(Boolean);
|
||||
log.VERBOSE("Device List: ", deviceList);
|
||||
return deviceList;
|
||||
}
|
||||
exports.getAudioDevices = getAudioDevices;
|
||||
|
||||
/**
|
||||
* Create and return the audio instance from the saved settings
|
||||
* TODO Allow the client to save and load these settings dynamically
|
||||
*
|
||||
* @returns new portAudio.AudioIO
|
||||
*/
|
||||
async function createAudioInstance() {
|
||||
const selectedDevice = await confirmAudioDevice({deviceId: getDeviceID()});//{deviceName: "VoiceMeeter VAIO3 Output (VB-Au"});
|
||||
log.DEBUG("Device selected from config: ", selectedDevice);
|
||||
// Create an instance of AudioIO with outOptions (defaults are as below), which will return a WritableStream
|
||||
|
||||
return new portAudio.AudioIO({
|
||||
inOptions: {
|
||||
channelCount: 2,
|
||||
sampleFormat: portAudio.SampleFormat16Bit,
|
||||
sampleRate: 48000,
|
||||
deviceId: selectedDevice.id, // Use -1 or omit the deviceId to select the default device
|
||||
closeOnError: false, // Close the stream if an audio error is detected, if set false then just log the error
|
||||
framesPerBuffer: 20, //(48000 / 1000) * 20, //(48000 * 16 * 2) / 1000 * 20 // (48000 * (16 / 8) * 2) / 60 / 1000 * 20 //0.025 * 48000 / 2
|
||||
highwaterMark: 3840,
|
||||
},
|
||||
});
|
||||
/*
|
||||
return new alsaInstance({
|
||||
channels: 2,
|
||||
format: "U8",
|
||||
rate: 48000,
|
||||
device: selectedDevice.name ?? "default", // Omit the deviceId to select the default device
|
||||
periodSize: 100, //(48000 / 1000) * 20, //(48000 * 16 * 2) / 1000 * 20 // (48000 * (16 / 8) * 2) / 60 / 1000 * 20 //0.025 * 48000 / 2
|
||||
periodTime: undefined,
|
||||
// highwaterMark: 3840
|
||||
});
|
||||
*/
|
||||
|
||||
}
|
||||
exports.createAudioInstance = createAudioInstance;
|
||||
@@ -1,85 +1,19 @@
|
||||
// Debug
|
||||
const { DebugBuilder } = require("../utilities/debugBuilder.js");
|
||||
const log = new DebugBuilder("client", "clientController");
|
||||
// Modules
|
||||
const { status, join, leave } = require("./commandController")
|
||||
|
||||
/**
|
||||
* Get an object of client guilds
|
||||
* @param req The express request which includes the discord client
|
||||
* @returns
|
||||
*/
|
||||
function getGuilds(req) {
|
||||
return req.discordClient.guilds.cache.map(guild => guild.id)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an object of the channels in a guild
|
||||
* @param {*} guildId The Guild ID to check the channels of
|
||||
* @param {*} req The request object to use to check the discord client
|
||||
*/
|
||||
function getChannels(guildId, req) {
|
||||
const guild = req.discordClient.guilds.find(guildId);
|
||||
log.DEBUG("Found Guild channels with guild", guild.channels, guild);
|
||||
return guild.channels;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see if a given guild has a given channel
|
||||
* @param {*} guildId The guild ID to check if the channel exists
|
||||
* @param {*} channelId The channel ID to check if exists in the guild
|
||||
* @param {*} req The express request param to use the discord client
|
||||
* @returns {true|false}
|
||||
*/
|
||||
function checkIfGuildHasChannel(guildId, channelId, req){
|
||||
const guildChannels = getChannels(guildId, req)
|
||||
const checkedChannel = guildChannels.find(c => c.id === channelId);
|
||||
if (!checkedChannel) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function getGuildFromChannel(channelId, req){
|
||||
const channel = req.discordClient.channels.cache.get(channelId);
|
||||
|
||||
if (!channel) return new Error("Error getting channel from client");
|
||||
|
||||
if (channel.guild) return channel.guild;
|
||||
|
||||
return new Error("No Guild found with the given ID");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Status of the discord process
|
||||
*/
|
||||
exports.getStatus = (req, res) => {
|
||||
log.INFO("Getting the status of the bot");
|
||||
guildIds = getGuilds(req);
|
||||
log.DEBUG("Guild IDs: ", guildIds);
|
||||
var guildStatuses = []
|
||||
for (const guildId of guildIds){
|
||||
status({guildID: guildId, callback: (statusObj) => {
|
||||
log.DEBUG("Status Object string: ", statusObj);
|
||||
guildStatuses.push(statusObj);
|
||||
}});
|
||||
}
|
||||
return res.status(200).json(guildStatuses);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the bot and join the server and preset specified
|
||||
*/
|
||||
exports.joinServer = (req, res) => {
|
||||
const channelId = req.body.channelID;
|
||||
const presetName = req.body.presetName;
|
||||
const guildObj = getGuildFromChannel(channelId, req);
|
||||
|
||||
if (!channelId || !presetName || !guildObj) return res.status(400).json({'message': "Request does not have all components to proceed"});
|
||||
|
||||
// join the sever
|
||||
join({guildID: guildObj.id, guildObj: guildObj, channelID: channelId, callback: () => {
|
||||
return res.sendStatus(202);
|
||||
}});
|
||||
log.INFO("Joining the server");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -87,12 +21,4 @@ exports.joinServer = (req, res) => {
|
||||
*/
|
||||
exports.leaveServer = (req, res) => {
|
||||
log.INFO("Leaving the server");
|
||||
const guildIds = getGuilds(req);
|
||||
log.DEBUG("Guild IDs: ", guildIds);
|
||||
for (const guildId of guildIds){
|
||||
leave({guildID: guildId, callback: (response) => {
|
||||
log.DEBUG("Response from leaving server on guild ID", guildId, response);
|
||||
}});
|
||||
}
|
||||
return res.sendStatus(202);
|
||||
}
|
||||
@@ -5,12 +5,11 @@ const log = new DebugBuilder("client", "clientController");
|
||||
require('dotenv').config();
|
||||
const modes = require("../config/modes");
|
||||
// Modules
|
||||
const { executeAsyncConsoleCommand } = require("../utilities/executeConsoleCommands.js");
|
||||
const { executeAsyncConsoleCommand, nodeObject } = require("../utilities/utilities");
|
||||
// Utilities
|
||||
const { updateId, updateConfig } = require("../utilities/updateConfig");
|
||||
const updatePreset = require("../utilities/updatePresets");
|
||||
const requests = require("../utilities/httpRequests");
|
||||
const { nodeObject } = require("../utilities/recordHelper.js");
|
||||
const {updatePreset, addNewPreset, getPresets, removePreset} = require("../utilities/updatePresets");
|
||||
const { onHttpError, requestOptions, sendHttpRequest } = require("../utilities/httpRequests");
|
||||
|
||||
var runningClientConfig = new nodeObject({_id: process.env.CLIENT_ID, _ip: process.env.CLIENT_IP, _name: process.env.CLIENT_NAME, _port: process.env.CLIENT_PORT, _location: process.env.CLIENT_LOCATION, _nearbySystems: process.env.CLIENT_NEARBY_SYSTEMS, _online: process.env.CLIENT_ONLINE});
|
||||
|
||||
@@ -34,8 +33,7 @@ function checkBodyForPresetFields(req, res, callback) {
|
||||
return callback();
|
||||
}
|
||||
|
||||
async function checkLocalIP() {
|
||||
let ipAddr;
|
||||
async function checkLocalIP() {
|
||||
if (process.platform === "win32") {
|
||||
// Windows
|
||||
var networkConfig = await executeAsyncConsoleCommand("ipconfig");
|
||||
@@ -98,28 +96,43 @@ exports.checkIn = async () => {
|
||||
let reqOptions;
|
||||
await this.checkConfig();
|
||||
// Check if there is an ID found, if not add the node to the server. If there was an ID, check in with the server to make sure it has the correct information
|
||||
if (runningClientConfig.id === 0) {
|
||||
// ID was not found in the config, creating a new node
|
||||
reqOptions = new requests.requestOptions("/nodes/newNode", "POST");
|
||||
requests.sendHttpRequest(reqOptions, JSON.stringify(), (responseObject) => {
|
||||
// Update the client's ID if the server accepted it
|
||||
if (responseObject.statusCode === 202) {
|
||||
runningClientConfig.id = responseObject.body.nodeId;
|
||||
updateId(responseObject.body.nodeId);
|
||||
}
|
||||
});
|
||||
try {
|
||||
if (runningClientConfig.id === 0) {
|
||||
// ID was not found in the config, creating a new node
|
||||
reqOptions = new requestOptions("/nodes/newNode", "POST");
|
||||
sendHttpRequest(reqOptions, JSON.stringify(), (responseObject) => {
|
||||
// Update the client's ID if the server accepted it
|
||||
if (responseObject.statusCode === 202) {
|
||||
runningClientConfig.id = responseObject.body.nodeId;
|
||||
updateId(responseObject.body.nodeId);
|
||||
}
|
||||
|
||||
if (responseObject.statusCode >= 300) {
|
||||
// Server threw an error
|
||||
onHttpError(responseObject.statusCode);
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
else {
|
||||
// ID is in the config, checking in with the server
|
||||
reqOptions = new requestOptions("/nodes/nodeCheckIn", "POST");
|
||||
sendHttpRequest(reqOptions, JSON.stringify(runningClientConfig), (responseObject) => {
|
||||
if (responseObject.statusCode === 202) {
|
||||
// Server accepted an update
|
||||
}
|
||||
if (responseObject.statusCode === 200) {
|
||||
// Server accepted the response but there were no keys to be updated
|
||||
}
|
||||
if (responseObject.statusCode >= 300) {
|
||||
// Server threw an error
|
||||
onHttpError(responseObject.statusCode);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
else {
|
||||
// ID is in the config, checking in with the server
|
||||
reqOptions = new requests.requestOptions("/nodes/nodeCheckIn", "POST");
|
||||
requests.sendHttpRequest(reqOptions, JSON.stringify(runningClientConfig), (responseObject) => {
|
||||
if (responseObject.statusCode === 202) {
|
||||
// Server accepted an update
|
||||
}
|
||||
if (responseObject.statusCode === 200) {
|
||||
// Server accepted the response but there were no keys to be updated
|
||||
}
|
||||
});
|
||||
catch (err) {
|
||||
log.ERROR("Error checking in: ", err);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,7 +148,7 @@ exports.requestCheckIn = async (req, res) => {
|
||||
* This is the endpoint wrapper to get the presets object
|
||||
*/
|
||||
exports.getPresets = async (req, res) => {
|
||||
return res.status(200).json(updatePreset.getPresets());
|
||||
return res.status(200).json(getPresets());
|
||||
}
|
||||
|
||||
/** Controller for the /client/updatePreset endpoint
|
||||
@@ -143,7 +156,7 @@ exports.getPresets = async (req, res) => {
|
||||
*/
|
||||
exports.updatePreset = async (req, res) => {
|
||||
checkBodyForPresetFields(req, res, () => {
|
||||
updatePreset.updatePreset(req.body.systemName, () => {
|
||||
updatePreset(req.body.systemName, () => {
|
||||
return res.sendStatus(200);
|
||||
}, {frequencies: req.body.frequencies, mode: req.body.mode, trunkFile: req.body.trunkFile});
|
||||
})
|
||||
@@ -154,10 +167,21 @@ exports.updatePreset = async (req, res) => {
|
||||
*/
|
||||
exports.addNewPreset = async (req, res) => {
|
||||
checkBodyForPresetFields(req, res, () => {
|
||||
updatePreset.addNewPreset(req.body.systemName, req.body.frequencies, req.body.mode, () => {
|
||||
addNewPreset(req.body.systemName, req.body.frequencies, req.body.mode, () => {
|
||||
return res.sendStatus(200);
|
||||
}, req.body.trunkFile);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a preset from the client
|
||||
*/
|
||||
exports.removePreset = async (req, res) => {
|
||||
checkBodyForPresetFields(req, res, () => {
|
||||
if (!req.body.systemName) return res.status("500").json({"message": "You must specify a system name to delete, this must match exactly to how the system name is saved."})
|
||||
removePreset(req.body.systemName, () => {
|
||||
return res.sendStatus(200);
|
||||
}, req.body.trunkFile);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,163 +0,0 @@
|
||||
require('dotenv').config();
|
||||
// Debug
|
||||
const { DebugBuilder } = require("../utilities/debugBuilder.js");
|
||||
const log = new DebugBuilder("client", "commandController");
|
||||
// Modules
|
||||
const { joinVoiceChannel, VoiceConnectionStatus, getVoiceConnection, createAudioPlayer, createAudioResource, AudioPlayerStatus } = require("@discordjs/voice");
|
||||
const { OpusEncoder } = require("@discordjs/opus");
|
||||
const { calcRmsSync } = require("../utilities/rmsCalculator.js");
|
||||
// Utilities
|
||||
const {replyToInteraction} = require("../utilities/messageHandler.js");
|
||||
const {createAudioInstance} = require("../controllers/audioController.js");
|
||||
const { all } = require('../routes/index.js');
|
||||
|
||||
// Declare the encoder
|
||||
const encoder = new OpusEncoder(48000, 2);
|
||||
const noiseGateOpen = process.env.AUDIO_NOISE_GATE_OPEN ?? 100;
|
||||
|
||||
/**
|
||||
* Join the specified voice channel
|
||||
*
|
||||
* @param interaction Message interaction from discord
|
||||
* @param {string||any} guildID The specified Guild ID if this function is run from the client instead of from an interaction in Discord
|
||||
* @param {string||any} channelID The channel ID to join
|
||||
* @param guild The guild object to be used to create a voice adapter
|
||||
* @param {function} callback The callback that will be needed if this function is run with a Guild ID instead of an interaction
|
||||
*/
|
||||
exports.join = async function join({interaction= undefined, guildID= undefined, channelID = undefined, guildObj = undefined, callback = undefined}){
|
||||
if (interaction){
|
||||
const voiceChannel = interaction.options.getChannel('voicechannel');
|
||||
channelID = voiceChannel.id;
|
||||
guildID = interaction.guildId;
|
||||
guildObj = interaction.guild;
|
||||
if (interaction) replyToInteraction(interaction, `Ok, Joining ${voiceChannel.name}`);
|
||||
}
|
||||
log.DEBUG("Channel ID: ", channelID)
|
||||
log.DEBUG("Guild ID: ", guildID)
|
||||
|
||||
const vcConnectionObj = {
|
||||
channelId: channelID,
|
||||
guildId: guildID,
|
||||
adapterCreator: guildObj.voiceAdapterCreator,
|
||||
selfMute: false,
|
||||
selfDeaf: false,
|
||||
};
|
||||
|
||||
// Join the voice channel
|
||||
const voiceConnection = await joinVoiceChannel(vcConnectionObj);
|
||||
|
||||
// Create the audio stream instance
|
||||
const audioInstance = await createAudioInstance();
|
||||
log.VERBOSE("Audio Instance: ", audioInstance);
|
||||
|
||||
// Play audio data when it's received from the stream
|
||||
audioInstance.on('data', buffer => {
|
||||
buffer = Buffer.from(buffer);
|
||||
//log.VERBOSE("Audio buffer: ", buffer);
|
||||
// Check intensity of audio and only play when audio is present (no white noise/static)
|
||||
volume = Math.trunc(calcRmsSync(buffer, buffer.length));
|
||||
|
||||
if (parseInt(volume) > parseInt(noiseGateOpen)) {
|
||||
//log.VERBOSE("Noisegate and buffer volume: ", (parseInt(volume) > parseInt(noiseGateOpen)), noiseGateOpen, volume);
|
||||
const encodedBuffer = encoder.encode(buffer);
|
||||
voiceConnection.playOpusPacket(encodedBuffer);
|
||||
}
|
||||
});
|
||||
|
||||
audioInstance.start();
|
||||
|
||||
// Handle state changes in the voice connection
|
||||
voiceConnection.on('stateChange', async (oldState, newState) => {
|
||||
//log.VERBOSE("VoiceConnection state Changed from state: ", oldState, "\n\nto state: ", newState);
|
||||
log.DEBUG("VoiceConnection state Changed from: ", oldState.status, "to: ", newState.status);
|
||||
|
||||
// Ready -> Connecting
|
||||
if (oldState.status === VoiceConnectionStatus.Ready && newState.status === VoiceConnectionStatus.Connecting) {
|
||||
log.DEBUG("Configuring Network");
|
||||
voiceConnection.configureNetworking();
|
||||
return;
|
||||
}
|
||||
|
||||
// Ready -> Disconnected
|
||||
if (oldState.status === VoiceConnectionStatus.Ready && newState.status === VoiceConnectionStatus.Disconnected) {
|
||||
log.DEBUG("Attempting to reconnect the voice connection");
|
||||
voiceConnection = joinVoiceChannel(vcConnectionObj);
|
||||
return;
|
||||
}
|
||||
|
||||
// Ready
|
||||
if (newState.status === VoiceConnectionStatus.Ready){
|
||||
log.DEBUG("Bot connected to voice channel");
|
||||
return;
|
||||
}
|
||||
|
||||
// Destroyed
|
||||
if (newState.status === VoiceConnectionStatus.Destroyed){
|
||||
log.DEBUG("Quitting audio instance");
|
||||
audioInstance.quit();
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
voiceConnection.on('error', async (error) => {
|
||||
log.ERROR("Voice Connection Error: ", error);
|
||||
})
|
||||
|
||||
if (guildID && callback) return callback();
|
||||
else return;
|
||||
}
|
||||
|
||||
/**
|
||||
* If in a voice channel for the specified guild, leave
|
||||
*
|
||||
* @param interaction Message interaction from discord
|
||||
* @param guildID
|
||||
* @param callback
|
||||
*/
|
||||
exports.leave = async function leave({interaction = undefined, guildID= undefined, callback = undefined}) {
|
||||
if(interaction) {
|
||||
guildID = interaction.guild.id;
|
||||
}
|
||||
const voiceConnection = getVoiceConnection(guildID);
|
||||
|
||||
let response;
|
||||
if (!voiceConnection){
|
||||
response = "Not in a voice channel."
|
||||
if (interaction) return replyToInteraction(interaction, response);
|
||||
else callback(response);
|
||||
}
|
||||
voiceConnection.destroy();
|
||||
|
||||
response = "Goodbye"
|
||||
if (interaction) return replyToInteraction(interaction, response);
|
||||
else callback(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the voice status of the bots
|
||||
* @param {*} param0
|
||||
* @returns
|
||||
*/
|
||||
exports.status = async function status({interaction= undefined, guildID= undefined, callback = undefined}) {
|
||||
//if (!interaction && !guildID) // Need error of sorts
|
||||
if (interaction){
|
||||
guildID = interaction.guild.id;
|
||||
}
|
||||
const voiceConnection = getVoiceConnection(guildID);
|
||||
|
||||
const statusObj = {
|
||||
"guildID": guildID,
|
||||
"voiceConnection": typeof g !== 'undefined' ? true : false // True if there is a voice connection, false if undefined
|
||||
}
|
||||
|
||||
log.DEBUG('Status Object: ', statusObj);
|
||||
|
||||
// get the status and return it accordingly (message reply / module)
|
||||
|
||||
if (interaction) {
|
||||
return replyToInteraction(interaction, "Pong! I have Aids and now you do too!");
|
||||
}
|
||||
else {
|
||||
callback(statusObj);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user