Initial removal of internal discord bot
This commit is contained in:
@@ -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
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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 });
|
||||
}
|
||||
}
|
||||
@@ -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 })
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -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 });
|
||||
}
|
||||
}
|
||||
@@ -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"}}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
})()
|
||||
}
|
||||
};
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
55
Client/utilities/utilities.js
Normal file
55
Client/utilities/utilities.js
Normal 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;
|
||||
Reference in New Issue
Block a user