Initial removal of internal discord bot

This commit is contained in:
Logan Cusano
2023-05-18 22:53:25 -04:00
parent 48999e0d63
commit e7b802839e
20 changed files with 176 additions and 758 deletions

View File

@@ -5,9 +5,7 @@ var cookieParser = require('cookie-parser');
var logger = require('morgan');
var http = require('http');
require('dotenv').config();
const fs = require('fs');
const { DebugBuilder } = require("./utilities/debugBuilder");
const deployCommands = require('./utilities/deployCommands');
const { checkIn } = require("./controllers/clientController");
var indexRouter = require('./routes/index');
@@ -16,28 +14,10 @@ var clientRouter = require('./routes/client');
var radioRouter = require('./routes/radio');
const log = new DebugBuilder("client", "app");
const {
Client,
Events,
Collection,
GatewayIntentBits,
MessageActionRow,
MessageButton
} = require('discord.js');
var app = express();
var discordToken = process.env.TOKEN;
var port = process.env.HTTP_PORT || '3010';
const discordClient = new Client({
intents: [
GatewayIntentBits.Guilds,
GatewayIntentBits.GuildMessages,
GatewayIntentBits.MessageContent,
GatewayIntentBits.GuildVoiceStates
]
});
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
@@ -53,10 +33,7 @@ app.use(express.static(path.join(__dirname, 'public')));
app.use('/', indexRouter);
// Discord bot control route
app.use('/bot', (req, res, next) => {
req.discordClient = discordClient; // Add the discord client to bot requests to be used downstream
next();
}, botRouter);
app.use('/bot', botRouter);
// Local client control route
app.use("/client", clientRouter);
@@ -116,54 +93,8 @@ async function runHTTPServer() {
})
}
// Discord bot config
// Setup commands for the Discord bot
discordClient.commands = new Collection();
const commandsPath = path.join(__dirname, 'commands');
const commandFiles = fs.readdirSync(commandsPath).filter(file => file.endsWith('.js'));
//const commandFiles = fs.readdirSync('./commands').filter(file => file.endsWith('.js'));
for (const file of commandFiles) {
const filePath = path.join(commandsPath, file);
const command = require(filePath);
log.DEBUG("Importing command: ", command.data.name);
// Set a new item in the Collection
// With the key as the command name and the value as the exported module
discordClient.commands.set(command.data.name, command);
}
// Run when the bot is ready
discordClient.on('ready', () => {
log.DEBUG(`Discord server up and running with client: ${discordClient.user.tag}`);
log.INFO(`Logged in as ${discordClient.user.tag}!`);
// Deploy slash commands
log.DEBUG("Deploying slash commands");
deployCommands.deploy(discordClient.user.id, discordClient.guilds.cache.map(guild => guild.id));
log.DEBUG(`Starting HTTP Server`);
log.DEBUG(`Starting HTTP Server`);
runHTTPServer();
log.DEBUG("Checking in with the master server")
checkIn();
});
// Setup any additional event handlers
const eventsPath = path.join(__dirname, 'events');
if (fs.existsSync(eventsPath)) {
const eventFiles = fs.readdirSync(eventsPath).filter(file => file.endsWith('.js'));
if (eventFiles.length > 0) {
for (const file of eventFiles) {
const filePath = path.join(eventsPath, file);
const event = require(filePath);
if (event.once) {
discordClient.once(event.name, (...args) => event.execute(...args));
} else {
discordClient.on(event.name, (...args) => event.execute(...args));
}
}
}
}
discordClient.login(discordToken); //Load Client Discord Token

View File

@@ -1,76 +0,0 @@
#!/usr/bin/env node
/**
* Module dependencies.
*/
const app = require('../app');
// Debug
const { DebugBuilder } = require("../utilities/debugBuilder.js");
const log = new DebugBuilder("client", "www");
const http = require('http');
const config = require('../config/clientConfig');
const clientController = require('../controllers/clientController');
/**
* Get port from environment and store in Express.
*/
app.set('port', config.clientConfig.port);
/**
* Create HTTP server.
*/
const server = http.createServer(app);
/**
* Listen on provided port, on all network interfaces.
*/
server.listen(config.clientConfig.port);
server.on('error', onError);
server.on('listening', onListening);
/**
* Event listener for HTTP server "error" event.
*/
function onError(error) {
if (error.syscall !== 'listen') {
throw error;
}
const bind = typeof port === 'string'
? 'Pipe ' + port
: 'Port ' + port;
// handle specific listen errors with friendly messages
switch (error.code) {
case 'EACCES':
console.error(bind + ' requires elevated privileges');
process.exit(1);
break;
case 'EADDRINUSE':
console.error(bind + ' is already in use');
process.exit(1);
break;
default:
throw error;
}
}
/**
* Event listener for HTTP server "listening" event.
*/
function onListening() {
const addr = server.address();
const bind = typeof addr === 'string'
? 'pipe ' + addr
: 'port ' + addr.port;
log.DEBUG('Listening on ' + bind);
// check in with the server to add this node or come back online
clientController.checkIn();
}

View File

@@ -1,17 +0,0 @@
// Debug
const { DebugBuilder } = require("../utilities/debugBuilder");
const log = new DebugBuilder("client", "ping");
// Modules
const { SlashCommandBuilder } = require('discord.js');
const { join } = require("../controllers/commandController")
module.exports = {
data: new SlashCommandBuilder()
.setName('join')
.setDescription('Join a voice channel'),
example: "join",
isPrivileged: false,
async execute(interaction) {
await join({ interaction: interaction });
}
}

View File

@@ -1,17 +0,0 @@
// Debug
const { DebugBuilder } = require("../utilities/debugBuilder.js");
const log = new DebugBuilder("client", "leave");
// Modules
const { SlashCommandBuilder } = require('discord.js');
const { leave } = require("../controllers/commandController")
module.exports = {
data: new SlashCommandBuilder()
.setName('leave')
.setDescription('Leave a voice channel'),
example: "leave",
isPrivileged: false,
async execute(interaction) {
await leave({ interaction: interaction })
}
}

View File

@@ -1,28 +0,0 @@
// Utilities
const { replyToInteraction } = require('../utilities/messageHandler.js');
const { SlashCommandBuilder } = require('discord.js');
const { DebugBuilder } = require("../utilities/debugBuilder");
const log = new DebugBuilder("client", "ping");
module.exports = {
data: new SlashCommandBuilder()
.setName('ping')
.setDescription('Replies with your input!'),
/*
.addStringOption(option =>
option.setName('input')
.setDescription('The input to echo back')
.setRequired(false)
.addChoices()),
*/
example: "ping",
isPrivileged: false,
async execute(interaction) {
try{
await replyToInteraction(interaction, "Pong! I have Aids and now you do too!"); // TODO - Add insults as the response to this command
}catch(err){
log.ERROR(err)
//await interaction.reply(err.toString());
}
}
};

View File

@@ -1,19 +0,0 @@
// Debug
const { DebugBuilder } = require("../utilities/debugBuilder.js");
const log = new DebugBuilder("client", "status");
// Modules
const { status } = require('../controllers/commandController');
// Utilities
const { SlashCommandBuilder } = require('discord.js');
module.exports = {
data: new SlashCommandBuilder()
.setName('status')
.setDescription('Check the status of the bot'),
example: "status",
isPrivileged: false,
async execute(interaction) {
await status({ interaction: interaction });
}
}

View File

@@ -1 +1 @@
{"Westchester Cty. Simulcast":{"frequencies":[470575000,470375000,470525000,470575000,470550000],"mode":"p25","trunkFile":"trunk.tsv"},"coppies":{"frequencies":[154875000],"mode":"nbfm","trunkFile":"none"}}
{"Westchester Cty. Simulcast":{"frequencies":[470575000,470375000,470525000,470575000,470550000],"mode":"p25","trunkFile":"trunk.tsv"},"coppies":{"frequencies":[154690000],"mode":"nbfm","trunkFile":"none"},"poopoo":{"frequencies":[479135500],"mode":"nbfm","trunkFile":"none"},"ppeeeeeeeeee":{"frequencies":[479135500,133990000,133000000,555999000],"mode":"p25","trunkFile":"none"}}

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -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);
});
}

View File

@@ -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);
}
}

View File

@@ -2,22 +2,21 @@
const express = require('express');
const router = express.Router();
// Controllers
const clientController = require("../controllers/clientController");
const {requestCheckIn, getPresets, updatePreset, addNewPreset, removePreset} = require("../controllers/clientController");
/** GET Request a check in from the client
* Queue the client to check in with the server
*
* The status of the checkin request: 200 = Queued
*/
router.get('/requestCheckIn', clientController.requestCheckIn);
router.get('/requestCheckIn', requestCheckIn);
/** GET Object of all known presets
* Query the client to get all the known presets
*/
router.get('/presets', clientController.getPresets);
router.get('/presets', getPresets);
/** POST Update to preset
* Join the channel specified listening to the specified freq/mode
*
* @param req The request sent from the master
* @param {string} req.body.systemName The name of the system to be updated
@@ -25,7 +24,7 @@ router.get('/presets', clientController.getPresets);
* @param {string} req.body.mode The listening mode for the SDR
* @param {string} req.body.trunkFile If the listening mode is digital this can be set to identify the communications
*/
router.post('/updatePreset', clientController.updatePreset);
router.post('/updatePreset', updatePreset);
/** POST Add new preset
* Join the channel specified listening to the specified freq/mode
@@ -36,6 +35,14 @@ router.post('/updatePreset', clientController.updatePreset);
* @param {string} req.body.mode The listening mode for the SDR
* @param {string} req.body.trunkFile If the listening mode is digital this can be set to identify the communications
*/
router.post('/addPreset', clientController.addNewPreset);
router.post('/addPreset', addNewPreset);
/** POST Remove a preset
*
* @param req The request sent from the master
* @param {string} req.body.systemName The name of the system to be updated
*/
router.post('/removePreset', removePreset);
module.exports = router;

View File

@@ -1,49 +0,0 @@
const { REST, Routes } = require('discord.js');
require('dotenv').config();
const token = process.env.TOKEN;
//const clientId = process.env.clientId;
//const guildId = process.env.guildId;
const fs = require('node:fs');
const path = require('node:path');
const { DebugBuilder } = require("./debugBuilder");
const log = new DebugBuilder("client", "deployCommands");
const commands = [];
// Grab all the command files from the commands directory you created earlier
const commandsPath = path.resolve(__dirname, '../commands');
const commandFiles = fs.readdirSync(commandsPath).filter(file => file.endsWith('.js'));
exports.deploy = (clientId, guildIDs) => {
log.DEBUG("Deploying commands for: ", guildIDs);
if (Array.isArray(guildIDs)) guildIDs = [guildIDs];
// Grab the SlashCommandBuilder#toJSON() output of each command's data for deployment
for (const file of commandFiles) {
const command = require(`${path.resolve(commandsPath, file)}`);
commands.push(command.data.toJSON());
}
// Construct and prepare an instance of the REST module
const rest = new REST({ version: '10' }).setToken(token);
// and deploy your commands!
for (const guildId of guildIDs){
(async () => {
try {
log.DEBUG(`Started refreshing ${commands.length} application (/) commands for guild ID: ${guildId}.`);
// The put method is used to fully refresh all commands in the guild with the current set
const data = await rest.put(
Routes.applicationGuildCommands(clientId, guildId),
{ body: commands },
);
log.DEBUG(`Successfully reloaded ${data.length} application (/) commands for guild ID: ${guildId}.`);
} catch (error) {
// And of course, make sure you catch and log any errors!
log.ERROR("ERROR Deploying commands: ", error, "Body from error: ", commands);
}
})()
}
};

View File

@@ -1,52 +0,0 @@
// Modules
const { promisify } = require('util');
const { exec } = require("child_process");
// Debug
const { DebugBuilder } = require("../utilities/debugBuilder.js");
// Global Vars
const log = new DebugBuilder("client", "executeConsoleCommands");
const execCommand = promisify(exec);
async function executeAsyncConsoleCommand(consoleCommand) {
// Check to see if the command is a real command
// TODO needs to be improved
const acceptableCommands = [ "arecord -L", 'ipconfig', 'ip addr' ];
if (!acceptableCommands.includes(consoleCommand)) {
log.WARN("Console command is not acceptable: ", consoleCommand);
return undefined;
}
log.DEBUG("Running console command: ", consoleCommand);
const tempOutput = await execCommand(consoleCommand);
const output = tempOutput.stdout.trim();
log.DEBUG("Executed Console Command Response: ", output)
// TODO add some error checking
return output;
}
exports.executeAsyncConsoleCommand = executeAsyncConsoleCommand;
async function returnAlsaDeviceObject() {
const listAlsaDevicesCommand = "arecord -L";
const commandResponse = await executeAsyncConsoleCommand(listAlsaDevicesCommand);
const brokenCommand = String(commandResponse).split('\n');
var devices = [];
var i = 0;
for (const responseLine of brokenCommand) {
if (String(responseLine) && !String(responseLine).match(/^\s/g)) {
const tempDevice = {
id: i,
name: responseLine
}
devices.push(tempDevice);
i += 1;
}
}
return devices;
}
exports.returnAlsaDeviceObject = returnAlsaDeviceObject;

View File

@@ -5,10 +5,9 @@ const log = new DebugBuilder("client", "httpRequests");
require('dotenv').config();
// Modules
const http = require("http");
const { nodeObject } = require("./recordHelper.js");
exports.requestOptions = class requestOptions {
constructor(path, method, hostname = undefined, headers = undefined, port = undefined) {
constructor(path, method, hostname = undefined, headers = undefined, port = undefined) {
if (method === "POST"){
this.hostname = hostname ?? process.env.SERVER_HOSTNAME ?? process.env.SERVER_IP;
this.path = path;
@@ -32,12 +31,22 @@ exports.sendHttpRequest = function sendHttpRequest(requestOptions, data, callbac
// Create the request
const req = http.request(requestOptions, res => {
res.on('data', (data) => {
const responseObject = {
"statusCode": res.statusCode,
"body": JSON.parse(data)
};
log.DEBUG("Response Object: ", responseObject);
callback(responseObject);
if (res.statusCode >= 200 && res.statusCode <= 299) {
const responseObject = {
"statusCode": res.statusCode,
"body": JSON.parse(data)
};
log.DEBUG("Response Object: ", responseObject);
callback(responseObject);
}
if (res.statusCode >= 300) {
const responseObject = {
"statusCode": res.statusCode,
"body": data
};
log.DEBUG("Response Object: ", responseObject);
callback(responseObject);
}
})
}).on('error', err => {
log.ERROR('Error: ', err.message)
@@ -47,4 +56,18 @@ exports.sendHttpRequest = function sendHttpRequest(requestOptions, data, callbac
// Write the data to the request and send it
req.write(data)
req.end()
}
exports.onHttpError = function onHttpError(httpStatusCode) {
switch(httpStatusCode){
case 404:
// Endpoint not found
log.WARN("404 received");
break;
default:
// Unhandled HTTP error code
log.ERROR("HTTP request returned with status: ", httpStatusCode)
break;
}
}

View File

@@ -1,9 +0,0 @@
// Debug
const { DebugBuilder } = require("../utilities/debugBuilder.js");
const log = new DebugBuilder("client", "messageHandler");
exports.replyToInteraction = async function replyToInteraction(interaction, message){
interaction.reply({ content: message, fetchReply: true })
.then((message) => log.DEBUG(`Reply sent with content ${message.content}`))
.catch((err) => log.ERROR(err));
}

View File

@@ -1,26 +0,0 @@
/**
*
*/
class nodeObject {
/**
*
* @param {*} param0._id The ID of the node
* @param {*} param0._name The name of the node
* @param {*} param0._ip The IP that the master can contact the node at
* @param {*} param0._port The port that the client is listening on
* @param {*} param0._location The physical location of the node
* @param {*} param0._online An integer representation of the online status of the bot, ie 0=off, 1=on
* @param {*} param0._nearbySystems An object array of nearby systems
*/
constructor({ _id = null, _name = null, _ip = null, _port = null, _location = null, _nearbySystems = null, _online = null }) {
this.id = _id;
this.name = _name;
this.ip = _ip;
this.port = _port;
this.location = _location;
this.nearbySystems = _nearbySystems;
this.online = _online;
}
}
exports.nodeObject = nodeObject;

View File

@@ -1,21 +0,0 @@
exports.calcRmsSync = (arr , n) => {
var square = 0;
var mean = 0;
var root = 0;
// Calculate square.
for (i = 0; i < n; i++) {
square += Math.pow(arr[i], 2);
}
// Calculate Mean.
mean = (square / (n));
// Calculate Root.
root = Math.sqrt(mean);
// Normalize the output
root = root / 10
return root;
}

View File

@@ -48,12 +48,14 @@ function convertFrequencyToHertz(frequency){
if (Number.isInteger(frequency)) {
log.DEBUG(`${frequency} is an integer.`);
// Check to see if the frequency has the correct length
if (frequency.toString().length >= 7 && frequency.toString().length <= 9) return frequency
if (frequency >= 1000000) return frequency
if (frequency >= 100 && frequency <= 999) return frequency * 1000000
log.WARN("Frequency hasn't matched filters: ", frequency);
}
else {
log.DEBUG(`${frequency} is a float value.`);
// Convert to a string to remove the decimal in place and then correct the length
return converter(frequency).from("MHz").to("Hz");
return parseInt(converter(frequency).from("MHz").to("Hz"));
}
} else {
log.DEBUG(`${frequency} is not a number`);
@@ -116,5 +118,20 @@ exports.updatePreset = (systemName, callback, { frequencies = undefined, mode =
}
}
/**
* Deletes the specified system
*
* @param {string} systemName The name of the system being modified
* @param {function} callback The callback function to be called when the function completes
*/
exports.removePreset = (systemName, callback) => {
const presets = this.getPresets();
// Check if a system name was passed
if (systemName in presets) {
delete presets[systemName];
writePresets(presets, callback);
}
}

View File

@@ -0,0 +1,55 @@
// Modules
const { promisify } = require('util');
const { exec } = require("child_process");
// Debug
const { DebugBuilder } = require("../utilities/debugBuilder.js");
// Global Vars
const log = new DebugBuilder("client", "executeConsoleCommands");
const execCommand = promisify(exec);
exports.executeAsyncConsoleCommand = async function executeAsyncConsoleCommand(consoleCommand) {
// Check to see if the command is a real command
// TODO needs to be improved
const acceptableCommands = [ "arecord -L", 'ipconfig', 'ip addr' ];
if (!acceptableCommands.includes(consoleCommand)) {
log.WARN("Console command is not acceptable: ", consoleCommand);
return undefined;
}
log.DEBUG("Running console command: ", consoleCommand);
const tempOutput = await execCommand(consoleCommand);
const output = tempOutput.stdout.trim();
log.DEBUG("Executed Console Command Response: ", output)
// TODO add some error checking
return output;
}
/**
*
*/
class nodeObject {
/**
*
* @param {*} param0._id The ID of the node
* @param {*} param0._name The name of the node
* @param {*} param0._ip The IP that the master can contact the node at
* @param {*} param0._port The port that the client is listening on
* @param {*} param0._location The physical location of the node
* @param {*} param0._online An integer representation of the online status of the bot, ie 0=off, 1=on
* @param {*} param0._nearbySystems An object array of nearby systems
*/
constructor({ _id = null, _name = null, _ip = null, _port = null, _location = null, _nearbySystems = null, _online = null }) {
this.id = _id;
this.name = _name;
this.ip = _ip;
this.port = _port;
this.location = _location;
this.nearbySystems = _nearbySystems;
this.online = _online;
}
}
exports.nodeObject = nodeObject;