23 Commits

Author SHA1 Message Date
Logan Cusano
597546b73d Ensure async functions wait for node contstructor 2023-06-18 22:35:51 -04:00
Logan Cusano
333e7420f4 Removed erroneous indexing on sqlreseponse 2023-06-18 22:32:01 -04:00
Logan Cusano
37a03c5cc6 Forgot to await getting new node 2023-06-18 22:29:16 -04:00
Logan Cusano
d2e9f286e2 Improved logging for server side add new node 2023-06-18 22:25:41 -04:00
Logan Cusano
255b1282ec Improve logging for server SQL requests 2023-06-18 22:22:20 -04:00
Logan Cusano
878e64fa42 Fix bug in return from addNewNode
- Return the function to get a node from the ID since the add new doesn't return node rows
- Update the controller to send the correct key from the updated object
2023-06-18 19:13:03 -04:00
Logan Cusano
7a040a8e97 Handle if new node is added with no nearby systems 2023-06-18 19:06:14 -04:00
Logan Cusano
8dffeccf83 Fix bug with 'connected' key when adding new node to the server 2023-06-18 19:02:15 -04:00
Logan Cusano
2108a3b92b Improved error response for new node 2023-06-18 17:13:29 -04:00
Logan Cusano
960b801dd2 Working on initial startup for new clients 2023-06-18 16:36:38 -04:00
Logan Cusano
dd5b442377 Update client clientController to better handle no ID 2023-06-18 16:26:55 -04:00
Logan Cusano
c5a7131063 Improve validation when checking for nodeId 2023-06-18 15:31:15 -04:00
Logan Cusano
5d54f07af4 Enabled nodeMonitorService
- Updated logging for nodeMonitorService to use it's own debugBuilder
2023-06-18 14:56:54 -04:00
Logan Cusano
24faa5279d #29
- Update debugBuilder to inspect objects instead of stringify
- Moved tthe debug entry for saving post to after the post is validated
2023-06-18 14:49:15 -04:00
Logan Cusano
79d2ca1887 #6 Removed copy of env
- Should be done manually and edited before running setup script
2023-06-17 23:37:26 -04:00
Logan Cusano
c2b4b4bfa1 #16
- Allow bots to display the preset they are listening to
2023-06-17 22:02:09 -04:00
Logan Cusano
d8a697e583 #13
- Added command to allow users to give other users their group
- Shows all groups as options but will not let user add another user if the caller doesn't have the role
2023-06-17 21:49:46 -04:00
Logan Cusano
44caa11f7c Fixed typo in remove description 2023-06-17 21:28:33 -04:00
Logan Cusano
dc92b07426 Implemented function to remove commands from a given bot and guild 2023-06-17 19:37:13 -04:00
Logan Cusano
92f4caad0c Improve the join node verification system 2023-06-17 19:25:47 -04:00
Logan Cusano
b888a9233d Improve the response when a user requests a bot to join 2023-06-17 19:01:55 -04:00
Logan Cusano
b4e27162aa Update to mysql2 2023-06-17 18:40:09 -04:00
Logan Cusano
bfda15866e Streamlined joining and leaving autocomplete
- Removed custom command builder as it's no longer needed with autocomplete
2023-06-17 17:55:27 -04:00
20 changed files with 235 additions and 145 deletions

View File

@@ -35,12 +35,12 @@ exports.joinServer = async (req, res) => {
log.INFO("Join requested to: ", deviceId, channelId, clientId, presetName, NGThreshold);
if (process.platform === "win32") {
log.DEBUG("Starting Windows Python");
pythonProcess = await spawn('python.exe', [resolve(__dirname, "../pdab/main.py"), deviceId, channelId, clientId, '-n', NGThreshold], { cwd: resolve(__dirname, "../pdab/").toString() });
pythonProcess = await spawn('python.exe', [resolve(__dirname, "../pdab/main.py"), deviceId, channelId, clientId, '-n', NGThreshold, '-p', presetName ], { cwd: resolve(__dirname, "../pdab/").toString() });
//pythonProcess = await spawn('C:\\Python310\\python.exe', [resolve(__dirname, "../PDAB/main.py"), deviceId, channelId, clientId, NGThreshold ]);
}
else {
log.DEBUG("Starting Linux Python");
pythonProcess = await spawn('python3', [resolve(__dirname, "../pdab/main.py"), deviceId, channelId, clientId,'-n', NGThreshold ], { cwd: resolve(__dirname, "../pdab/") });
pythonProcess = await spawn('python3', [resolve(__dirname, "../pdab/main.py"), deviceId, channelId, clientId,'-n', NGThreshold, '-p', presetName ], { cwd: resolve(__dirname, "../pdab/") });
}
log.VERBOSE("Python Process: ", pythonProcess);

View File

@@ -67,6 +67,11 @@ async function checkLocalIP() {
* Checks the config file for all required fields or gets and updates the required fields
*/
exports.checkConfig = async function checkConfig() {
if (!runningClientConfig.id || runningClientConfig.id == 0 || runningClientConfig.id == '0') {
updateConfig('id', "");
runningClientConfig.id = null;
}
if (!runningClientConfig.ip) {
const ipAddr = await checkLocalIP();
updateConfig('ip', ipAddr);
@@ -97,10 +102,10 @@ exports.checkIn = async () => {
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
try {
if (runningClientConfig.id === 0) {
if (!runningClientConfig?.id || runningClientConfig.id == null) {
// ID was not found in the config, creating a new node
reqOptions = new requestOptions("/nodes/newNode", "POST");
sendHttpRequest(reqOptions, JSON.stringify(), (responseObject) => {
sendHttpRequest(reqOptions, JSON.stringify({}), (responseObject) => {
// Update the client's ID if the server accepted it
if (responseObject.statusCode === 202) {
runningClientConfig.id = responseObject.body.nodeId;
@@ -109,6 +114,7 @@ exports.checkIn = async () => {
if (responseObject.statusCode >= 300) {
// Server threw an error
log.DEBUG("HTTP Error: ", responseObject);
onHttpError(responseObject.statusCode);
}
@@ -118,6 +124,7 @@ exports.checkIn = async () => {
// ID is in the config, checking in with the server
reqOptions = new requestOptions("/nodes/nodeCheckIn", "POST");
sendHttpRequest(reqOptions, JSON.stringify(runningClientConfig), (responseObject) => {
log.DEBUG("Check In Respose: ", responseObject);
if (responseObject.statusCode === 202) {
// Server accepted an update
}

View File

@@ -1,5 +1,5 @@
import argparse, platform, os
from discord import Intents, Client, Member, opus
from discord import Intents, Client, Member, opus, Activity, ActivityType
from discord.ext import commands
from NoiseGatev2 import NoiseGate
@@ -25,7 +25,7 @@ async def load_opus():
return "armv7l"
def main(clientId='OTQzNzQyMDQwMjU1MTE1MzA0.Yg3eRA.ZxEbRr55xahjfaUmPY8pmS-RHTY', channelId=367396189529833476, NGThreshold=50, deviceId=1):
def main(clientId='OTQzNzQyMDQwMjU1MTE1MzA0.Yg3eRA.ZxEbRr55xahjfaUmPY8pmS-RHTY', channelId=367396189529833476, NGThreshold=50, deviceId=1, presence="the radio"):
intents = Intents.default()
client = commands.Bot(command_prefix='!', intents=intents)
@@ -33,6 +33,8 @@ def main(clientId='OTQzNzQyMDQwMjU1MTE1MzA0.Yg3eRA.ZxEbRr55xahjfaUmPY8pmS-RHTY',
@client.event
async def on_ready():
print(f'We have logged in as {client.user}')
# Set the presence of the bot (what it's listening to)
await client.change_presence(activity=Activity(type=ActivityType.listening, name=presence))
channelIdToJoin = client.get_channel(channelId)
print("Channel", channelIdToJoin)
@@ -55,21 +57,24 @@ def main(clientId='OTQzNzQyMDQwMjU1MTE1MzA0.Yg3eRA.ZxEbRr55xahjfaUmPY8pmS-RHTY',
client.run(clientId)
parser = argparse.ArgumentParser()
parser.add_argument("deviceId", type=int, help="The ID of the audio device to use")
parser.add_argument("channelId", type=int, help="The ID of the voice channel to use")
parser.add_argument("clientId", type=str, help="The discord client ID")
parser.add_argument("-n", "--NGThreshold", type=int, help="Change the noisegate threshold. This defaults to 50")
args = parser.parse_args()
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("deviceId", type=int, help="The ID of the audio device to use")
parser.add_argument("channelId", type=int, help="The ID of the voice channel to use")
parser.add_argument("clientId", type=str, help="The discord client ID")
parser.add_argument("-n", "--NGThreshold", type=int, help="Change the noisegate threshold. This defaults to 50")
parser.add_argument("-p", "--presence", type=str, help="What the bot should be listening to")
args = parser.parse_args()
if (not args.NGThreshold):
args.NGThreshold = 50
if (not args.NGThreshold):
args.NGThreshold = 50
print("Arguments:", args)
print("Arguments:", args)
main(
clientId=args.clientId,
channelId=args.channelId,
NGThreshold=args.NGThreshold,
deviceId=args.deviceId
)
main(
clientId=args.clientId,
channelId=args.channelId,
NGThreshold=args.NGThreshold,
deviceId=args.deviceId,
presence=args.presence
)

View File

@@ -13,9 +13,6 @@ ls -ld $SCRIPT_DIR | awk '{print $3}' >> ./config/installerName
useradd -M RadioNode
usermod -s -L RadioNode
# Create the .env file from the example
cp $SCRIPT_DIR/.env.example $SCRIPT_DIR/.env
# Change the ownership of the directory to the service user
chown RadioNode -R $SCRIPT_DIR

View File

@@ -4,6 +4,7 @@ const debug = require('debug');
require('dotenv').config();
// Modules
const { writeFile } = require('fs');
const { inspect } = require('util');
const logLocation = process.env.LOG_LOCATION;
@@ -34,31 +35,31 @@ exports.DebugBuilder = class DebugBuilder {
this.INFO = (...messageParts) => {
const _info = debug(`${appName}:${fileName}:INFO`);
_info(messageParts);
writeToLog(`${appName}:${fileName}:INFO\t-\t${messageParts.map((messagePart, index, array) => {return JSON.stringify(messagePart)})}`, appName);
writeToLog(`${Date.now().toLocaleString('en-US', { timeZone: 'America/New_York' })} - ${appName}:${fileName}:INFO\t-\t${messageParts.map((messagePart, index, array) => {return inspect(messagePart)})}`, appName);
}
this.DEBUG = (...messageParts) => {
const _debug = debug(`${appName}:${fileName}:DEBUG`);
_debug(messageParts);
writeToLog(`${appName}:${fileName}:DEBUG\t-\t${messageParts.map((messagePart, index, array) => {return JSON.stringify(messagePart)})}`, appName);
writeToLog(`${Date.now().toLocaleString('en-US', { timeZone: 'America/New_York' })} - ${appName}:${fileName}:DEBUG\t-\t${messageParts.map((messagePart, index, array) => {return inspect(messagePart)})}`, appName);
}
this.VERBOSE = (...messageParts) => {
const _verbose = debug(`${appName}:${fileName}:VERBOSE`);
_verbose(messageParts);
writeToLog(`${appName}:${fileName}:VERBOSE\t-\t${messageParts.map((messagePart, index, array) => {return JSON.stringify(messagePart)})}`, appName);
writeToLog(`${Date.now().toLocaleString('en-US', { timeZone: 'America/New_York' })} - ${appName}:${fileName}:VERBOSE\t-\t${messageParts.map((messagePart, index, array) => {return inspect(messagePart)})}`, appName);
}
this.WARN = (...messageParts) => {
const _warn = debug(`${appName}:${fileName}:WARNING`);
_warn(messageParts);
writeToLog(`${appName}:${fileName}:WARNING\t-\t${messageParts.map((messagePart, index, array) => {return JSON.stringify(messagePart)})}`, appName);
writeToLog(`${Date.now().toLocaleString('en-US', { timeZone: 'America/New_York' })} - ${appName}:${fileName}:WARNING\t-\t${messageParts.map((messagePart, index, array) => {return inspect(messagePart)})}`, appName);
}
this.ERROR = (...messageParts) => {
const _error = debug(`${appName}:${fileName}:ERROR`);
_error(messageParts);
writeToLog(`${appName}:${fileName}:ERROR\t-\t${messageParts.map((messagePart, index, array) => {return JSON.stringify(messagePart)})}`, appName);
writeToLog(`${Date.now().toLocaleString('en-US', { timeZone: 'America/New_York' })} - ${appName}:${fileName}:ERROR\t-\t${messageParts.map((messagePart, index, array) => {return inspect(messagePart)})}`, appName);
if (process.env.EXIT_ON_ERROR && process.env.EXIT_ON_ERROR > 0) {
writeToLog("!--- EXITING ---!", appName);
setTimeout(process.exit, process.env.EXIT_ON_ERROR_DELAY ?? 0);

View File

@@ -0,0 +1,52 @@
const { SlashCommandBuilder } = require('discord.js');
const { DebugBuilder } = require("../utilities/debugBuilder");
const log = new DebugBuilder("server", "give-role");
module.exports = {
data: new SlashCommandBuilder()
.setName('give-role')
.setDescription('Use this command to give a role you have to another member.')
.addUserOption(option =>
option.setName('user')
.setDescription('The user you wish to give the role to ')
.setRequired(true))
.addRoleOption(option =>
option.setName('role')
.setDescription('The role you wish to give the selected user')
.setRequired(true)),
example: "give-role",
isPrivileged: false,
requiresTokens: false,
defaultTokenUsage: 0,
deferInitialReply: true,
/*async autocomplete(interaction) {
const focusedValue = interaction.options.getFocused();
},*/
async execute(interaction) {
try{
// The role to give to the user
const selectedRole = interaction.options.getRole('role');
// The user who should be given the role
var selectedUser = interaction.options.getUser("user");
selectedUser = interaction.guild.members.cache.get(selectedUser.id);
// The user who initiated the command
const initUser = interaction.member;
log.DEBUG("Give Role DEBUG: ", initUser, selectedRole, selectedUser);
// Check if the user has the role selected
if (!initUser.roles.cache.find(role => role.name === selectedRole.name)) return await interaction.editReply(`Sorry ${initUser}, you don't have the group ${selectedRole} and thus you cannot give it to ${selectedUser}`);
// Give the selected user the role and let both the user and the initiator know
await selectedUser.roles.add(selectedRole);
return await interaction.editReply(`Ok ${initUser}, ${selectedUser} has been given the ${selectedRole} role!`)
}catch(err){
log.ERROR(err)
//await interaction.reply(err.toString());
}
}
};

View File

@@ -1,9 +1,9 @@
// Modules
const { customSlashCommandBuilder } = require('../utilities/customSlashCommandBuilder');
const { SlashCommandBuilder } = require('discord.js');
const { DebugBuilder } = require("../utilities/debugBuilder");
const { getMembersInRole, getAllClientIds } = require("../utilities/utils");
const { getMembersInRole, getAllClientIds, filterAutocompleteValues } = require("../utilities/utils");
const { requestOptions, sendHttpRequest } = require("../utilities/httpRequests");
const { getOnlineNodes, updateNodeInfo, addNodeConnection, getConnectionByNodeId } = require("../utilities/mysqlHandler");
const { getOnlineNodes, updateNodeInfo, addNodeConnection, getConnectionByNodeId, getAllConnections } = require("../utilities/mysqlHandler");
// Global Vars
const log = new DebugBuilder("server", "join");
@@ -13,10 +13,10 @@ const log = new DebugBuilder("server", "join");
*
* @param {*} presetName The preset name to listen to on the client
* @param {*} channelId The channel ID to join the bot to
* @param {*} clientIdsUsed EITHER A collection of clients that are currently connected OR a single discord client ID (NOT dev portal ID) that should be used to join the server with
* @param {*} connections EITHER A collection of clients that are currently connected OR a single discord client ID (NOT dev portal ID) that should be used to join the server with
* @returns
*/
async function joinServerWrapper(presetName, channelId, clientIdsUsed) {
async function joinServerWrapper(presetName, channelId, connections) {
// Get nodes online
var onlineNodes = await new Promise((recordResolve, recordReject) => {
getOnlineNodes((nodeRows) => {
@@ -45,16 +45,16 @@ async function joinServerWrapper(presetName, channelId, clientIdsUsed) {
log.DEBUG("All clients: ", Object.keys(availableClientIds));
var selectedClientId;
if (typeof clientIdsUsed === 'string') {
if (typeof connections === 'string') {
for (const availableClientId of availableClientIds) {
if (availableClientId.discordId != clientIdsUsed ) selectedClientId = availableClientId;
if (availableClientId.discordId != connections ) selectedClientId = availableClientId;
}
}
else {
log.DEBUG("Client IDs Used: ", clientIdsUsed.keys());
for (const usedClientId of clientIdsUsed.keys()) {
log.DEBUG("Used Client ID: ", usedClientId);
availableClientIds = availableClientIds.filter(cid => cid.discordId != usedClientId);
log.DEBUG("Open connections: ", connections);
for (const connection of connections) {
log.DEBUG("Used Client ID: ", connection);
availableClientIds = availableClientIds.filter(cid => cid.discordId != connection.clientObject.discordId);
}
log.DEBUG("Available Client IDs: ", availableClientIds);
@@ -84,19 +84,48 @@ async function joinServerWrapper(presetName, channelId, clientIdsUsed) {
const nodeConnection = await addNodeConnection(selectedNode, selectedClientId);
log.DEBUG("Node Connection: ", nodeConnection);
});
return selectedClientId;
}
exports.joinServerWrapper = joinServerWrapper;
module.exports = {
data: new customSlashCommandBuilder()
data: new SlashCommandBuilder()
.setName('join')
.setDescription('Join the channel you are in with the preset you choose')
.addAllSystemPresetOptions(),
.addStringOption(option =>
option.setName("preset")
.setDescription("The preset you would like to listen to")
.setAutocomplete(true)
.setRequired(true)),
example: "join",
isPrivileged: false,
requiresTokens: false,
defaultTokenUsage: 0,
deferInitialReply: true,
async autocomplete(interaction) {
const nodeObjects = await new Promise((recordResolve, recordReject) => {
getOnlineNodes((nodeRows) => {
recordResolve(nodeRows);
});
});
log.DEBUG("Node objects: ", nodeObjects);
var presetsAvailable = [];
for (const nodeObject of nodeObjects) {
log.DEBUG("Node object: ", nodeObject);
for (const presetName in nodeObject.nearbySystems) presetsAvailable.push(nodeObject.nearbySystems[presetName]);
}
log.DEBUG("All Presets available: ", presetsAvailable);
// Remove duplicates
options = [...new Set(presetsAvailable)];
log.DEBUG("DeDuped Presets available: ", options);
// Filter the results to what the user is entering
filterAutocompleteValues(interaction, options);
},
async execute(interaction) {
try{
const guildId = interaction.guild.id;
@@ -105,12 +134,13 @@ module.exports = {
const channelId = interaction.member.voice.channel.id;
log.DEBUG(`Join requested by: ${interaction.user.username}, to: '${presetName}', in channel: ${channelId} / ${guildId}`);
const onlineBots = await getMembersInRole(interaction);
const connections = await getAllConnections();
log.DEBUG("Online Bots: ", onlineBots);
log.DEBUG("Current Connections: ", connections);
await joinServerWrapper(presetName, channelId, onlineBots.online);
await interaction.editReply('**Pong.**');
const selectedClientId = await joinServerWrapper(presetName, channelId, connections);
await interaction.editReply(`Ok, ${interaction.member}. **${selectedClientId.name}** is joining your channel.`);
//await interaction.channel.send('**Pong.**'); // This will send a message to the channel of the interaction outside of the initial reply
}catch(err){
log.ERROR(err)

View File

@@ -1,13 +1,12 @@
// Modules
const { customSlashCommandBuilder } = require('../utilities/customSlashCommandBuilder');
const { SlashCommandBuilder } = require('discord.js');
const { DebugBuilder } = require("../utilities/debugBuilder");
const { getAllClientIds, getKeyByArrayValue } = require("../utilities/utils");
const { getAllClientIds, getKeyByArrayValue, filterAutocompleteValues } = require("../utilities/utils");
const { requestOptions, sendHttpRequest } = require("../utilities/httpRequests");
const { checkNodeConnectionByClientId, removeNodeConnectionByNodeId, updateNodeInfo, getConnectedNodes, getAllConnections } = require('../utilities/mysqlHandler');
const { checkNodeConnectionByClientId, removeNodeConnectionByNodeId, getAllConnections } = require('../utilities/mysqlHandler');
// Global Vars
const log = new DebugBuilder("server", "leave");
const logAC = new DebugBuilder("server", "leave_autocorrect");
async function leaveServerWrapper(clientIdObject) {
if (!clientIdObject.clientId || !clientIdObject.name) return log.ERROR("Tried to leave server without client ID and/or Name");
@@ -34,7 +33,7 @@ async function leaveServerWrapper(clientIdObject) {
exports.leaveServerWrapper = leaveServerWrapper;
module.exports = {
data: new customSlashCommandBuilder()
data: new SlashCommandBuilder()
.setName('leave')
.setDescription('Disconnect a bot from the server')
.addStringOption(option =>
@@ -48,17 +47,12 @@ module.exports = {
defaultTokenUsage: 0,
deferInitialReply: true,
async autocomplete(interaction) {
const focusedValue = interaction.options.getFocused();
const connections = await getAllConnections();
const filtered = connections.filter(conn => String(conn.clientObject.name).startsWith(focusedValue)).map(conn => conn.clientObject.name);
logAC.DEBUG("Focused Value: ", focusedValue, connections, filtered);
await interaction.respond(
filtered.map(option => ({ name: option, value: option })),
);
const options = connections.map(conn => conn.clientObject.name);
await filterAutocompleteValues(interaction, options);
},
async execute(interaction) {
try{
const guildId = interaction.guild.id;
const botName = interaction.options.getString('bot');
log.DEBUG("Bot Name: ", botName)
const clinetIds = await getAllClientIds();

View File

@@ -7,7 +7,7 @@ const log = new DebugBuilder("server", "remove");
module.exports = {
data: new SlashCommandBuilder()
.setName('remove')
.setDescription('Remove an RSS source by it\' title')
.setDescription('Remove an RSS source by it\'s title')
.addStringOption(option =>
option.setName('title')
.setDescription('The title of the source to remove')

View File

@@ -25,7 +25,7 @@ exports.listAllNodes = async (req, res) => {
// Add a new node to the storage
exports.newNode = async (req, res) => {
if (!req.body.name) return res.send(400)
if (!req.body.name) return res.status(400).json("No name specified for new node");
try {
// Try to add the new user with defaults if missing options
@@ -38,9 +38,9 @@ exports.newNode = async (req, res) => {
_online: req.body.online ?? 0
});
addNewNode(newNode, (queryResults) => {
addNewNode(newNode, (newNodeObject) => {
// Send back a success if the user has been added and the ID for the client to keep track of
res.status(202).json({"nodeId": queryResults.insertId});
res.status(202).json({"nodeId": newNodeObject.id});
})
}
catch (err) {
@@ -108,6 +108,7 @@ exports.requestNodeJoinServer = async (req, res) => {
*/
exports.nodeMonitorService = class nodeMonitorService {
constructor() {
this.log = new DebugBuilder("server", "nodeMonitorService");
}
async start(){
@@ -130,21 +131,21 @@ exports.nodeMonitorService = class nodeMonitorService {
async checkInWithOnlineNodes(){
getOnlineNodes((nodes) => {
log.DEBUG("Online Nodes: ", nodes);
this.log.DEBUG("Online Nodes: ", nodes);
for (const node of nodes) {
const reqOptions = new requestOptions("/client/requestCheckIn", "GET", node.ip, node.port)
const request = sendHttpRequest(reqOptions, "", (responseObj) => {
sendHttpRequest(reqOptions, "", (responseObj) => {
if (responseObj) {
log.DEBUG("Response from: ", node.name, responseObj);
this.log.DEBUG("Response from: ", node.name, responseObj);
}
else {
log.DEBUG("No response from node, assuming it's offline");
this.log.DEBUG("No response from node, assuming it's offline");
const offlineNode = new nodeObject({ _online: 0, _id: node.id });
log.DEBUG("Offline node update object: ", offlineNode);
this.log.DEBUG("Offline node update object: ", offlineNode);
updateNodeInfo(offlineNode, (sqlResponse) => {
if (!sqlResponse) log.ERROR("No response from SQL object");
if (!sqlResponse) this.log.ERROR("No response from SQL object");
log.DEBUG("Updated node: ", sqlResponse);
this.log.DEBUG("Updated offline node: ", sqlResponse);
})
}
})

View File

@@ -148,7 +148,7 @@ client.on('ready', () => {
runHTTPServer();
log.DEBUG("Starting Node Monitoring Service");
//runNodeMonitorService();
runNodeMonitorService();
log.DEBUG("Starting RSS watcher");
runRssService();

View File

@@ -5,7 +5,7 @@ const { FeedStorage, PostStorage } = require("./libStorage");
const libUtils = require("./libUtils");
const { DebugBuilder } = require("./utilities/debugBuilder");
const log = new DebugBuilder("server", "libCore");
const mysql = require("mysql");
const mysql = require("mysql2");
const UserAgent = require("user-agents");
process.env.USER_AGENT_STRING = new UserAgent({ platform: 'Win32' }).toString();

View File

@@ -8,7 +8,7 @@ const { RSSSourceRecord, RSSPostRecord } = require("./utilities/recordHelper");
// Storage Specific Modules
// MySQL
const mysql = require("mysql");
const mysql = require("mysql2");
const rssFeedsTable = process.env.DB_RSS_FEEDS_TABLE;
const rssPostsTable = process.env.DB_RSS_POSTS_TABLE;
@@ -481,10 +481,10 @@ exports.PostStorage = class PostStorage extends Storage {
savePost(_postObject, callback){
const tempCreationDate = returnMysqlTime();
log.DEBUG("Saving Post Object:", _postObject);
if (!_postObject?.postId || !_postObject?.link) {
return callback(new Error("Post object malformed, check the object before saving it", _postObject), undefined)
}
log.DEBUG("Saving Post:", _postObject);
if (_postObject.link.length > 250) _postObject.link = _postObject.link.substring(0, 250);

View File

@@ -21,7 +21,7 @@
"jsdoc": "^4.0.2",
"jsonfile": "^6.1.0",
"morgan": "^1.10.0",
"mysql": "^2.18.1",
"mysql2": "^3.3.5",
"node-html-markdown": "^1.3.0",
"node-html-parser": "^6.1.5",
"openai": "^3.2.1",

View File

@@ -1,46 +0,0 @@
const { SlashCommandBuilder, SlashCommandStringOption } = require('discord.js');
const { DebugBuilder } = require("../utilities/debugBuilder");
const { BufferToJson } = require("../utilities/utils");
const log = new DebugBuilder("server", "customSlashCommandBuilder");
const { getAllNodes, getAllNodesSync } = require("../utilities/mysqlHandler");
exports.customSlashCommandBuilder = class customSlashCommandBuilder extends SlashCommandBuilder {
constructor() {
super();
}
async addAllSystemPresetOptions() {
const nodeObjects = await new Promise((recordResolve, recordReject) => {
getAllNodes((nodeRows) => {
recordResolve(nodeRows);
});
});
log.DEBUG("Node objects: ", nodeObjects);
var presetsAvailable = [];
for (const nodeObject of nodeObjects) {
log.DEBUG("Node object: ", nodeObject);
for (const presetName in nodeObject.nearbySystems) presetsAvailable.push(nodeObject.nearbySystems[presetName]);
}
log.DEBUG("All Presets available: ", presetsAvailable);
// Remove duplicates
presetsAvailable = [...new Set(presetsAvailable)];
log.DEBUG("DeDuped Presets available: ", presetsAvailable);
this.addStringOption(option => option.setName("preset").setRequired(true).setDescription("The channels"));
for (const preset of presetsAvailable){
log.DEBUG("Preset: ", preset);
this.options[0].addChoices({
'name': String(preset),
'value': String(preset)
});
}
log.DEBUG("Preset Options: ", this);
return this;
}
}

View File

@@ -4,6 +4,7 @@ const debug = require('debug');
require('dotenv').config();
// Modules
const { writeFile } = require('fs');
const { inspect } = require('util');
const logLocation = process.env.LOG_LOCATION;
@@ -34,31 +35,31 @@ exports.DebugBuilder = class DebugBuilder {
this.INFO = (...messageParts) => {
const _info = debug(`${appName}:${fileName}:INFO`);
_info(messageParts);
writeToLog(`${appName}:${fileName}:INFO\t-\t${messageParts.map((messagePart, index, array) => {return JSON.stringify(messagePart)})}`, appName);
writeToLog(`${Date.now().toLocaleString('en-US', { timeZone: 'America/New_York' })} - ${appName}:${fileName}:INFO\t-\t${messageParts.map((messagePart, index, array) => {return inspect(messagePart)})}`, appName);
}
this.DEBUG = (...messageParts) => {
const _debug = debug(`${appName}:${fileName}:DEBUG`);
_debug(messageParts);
writeToLog(`${appName}:${fileName}:DEBUG\t-\t${messageParts.map((messagePart, index, array) => {return JSON.stringify(messagePart)})}`, appName);
writeToLog(`${Date.now().toLocaleString('en-US', { timeZone: 'America/New_York' })} - ${appName}:${fileName}:DEBUG\t-\t${messageParts.map((messagePart, index, array) => {return inspect(messagePart)})}`, appName);
}
this.VERBOSE = (...messageParts) => {
const _verbose = debug(`${appName}:${fileName}:VERBOSE`);
_verbose(messageParts);
writeToLog(`${appName}:${fileName}:VERBOSE\t-\t${messageParts.map((messagePart, index, array) => {return JSON.stringify(messagePart)})}`, appName);
writeToLog(`${Date.now().toLocaleString('en-US', { timeZone: 'America/New_York' })} - ${appName}:${fileName}:VERBOSE\t-\t${messageParts.map((messagePart, index, array) => {return inspect(messagePart)})}`, appName);
}
this.WARN = (...messageParts) => {
const _warn = debug(`${appName}:${fileName}:WARNING`);
_warn(messageParts);
writeToLog(`${appName}:${fileName}:WARNING\t-\t${messageParts.map((messagePart, index, array) => {return JSON.stringify(messagePart)})}`, appName);
writeToLog(`${Date.now().toLocaleString('en-US', { timeZone: 'America/New_York' })} - ${appName}:${fileName}:WARNING\t-\t${messageParts.map((messagePart, index, array) => {return inspect(messagePart)})}`, appName);
}
this.ERROR = (...messageParts) => {
const _error = debug(`${appName}:${fileName}:ERROR`);
_error(messageParts);
writeToLog(`${appName}:${fileName}:ERROR\t-\t${messageParts.map((messagePart, index, array) => {return JSON.stringify(messagePart)})}`, appName);
writeToLog(`${Date.now().toLocaleString('en-US', { timeZone: 'America/New_York' })} - ${appName}:${fileName}:ERROR\t-\t${messageParts.map((messagePart, index, array) => {return inspect(messagePart)})}`, appName);
if (process.env.EXIT_ON_ERROR && process.env.EXIT_ON_ERROR > 0) {
writeToLog("!--- EXITING ---!", appName);
setTimeout(process.exit, process.env.EXIT_ON_ERROR_DELAY ?? 0);

View File

@@ -11,14 +11,14 @@ const path = require('node:path');
const { DebugBuilder } = require("./debugBuilder");
const log = new DebugBuilder("server", "deployCommands");
const commands = [];
var 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];
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)}`);
@@ -48,3 +48,35 @@ exports.deploy = (clientId, guildIDs) => {
})()
}
};
/**
* Remove all commands for a given bot in a given guild
*
* @param {*} clientId The client ID of the bot to remove commands from
* @param {*} guildId The ID of the guild to remove the bot commands from
*/
exports.removeAll = (clientId, guildId) => {
if (!Array.isArray(guildId)) guildIDs = [guildId];
log.DEBUG("Removing commands for: ", clientId, guildIDs);
commands = [];
const rest = new REST({ version: '10' }).setToken(token);
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,5 +1,5 @@
require('dotenv').config();
const mysql = require('mysql');
const mysql = require('mysql2');
const utils = require('./utils');
const { nodeObject, clientObject, connectionObject } = require("./recordHelper");
const { DebugBuilder } = require("../utilities/debugBuilder");
@@ -92,7 +92,7 @@ exports.getAllNodesSync = async () => {
console.log("Rows: ", rows);
return returnNodeObjectFromRows(rows);
return await returnNodeObjectFromRows(rows);
}
/** Get all nodes that have the online status set true (are online)
@@ -110,7 +110,7 @@ exports.getOnlineNodes = (callback) => {
* @param callback Callback function
*/
async function getNodeInfoFromId(nodeId, callback = undefined) {
if (!nodeId) throw new Error("No node ID given when trying to fetch node");
if (!nodeId || nodeId == '0' || nodeId == 0 ) throw new Error("No node ID given when trying to fetch node");
log.DEBUG("Getting node from ID: ", nodeId);
const sqlQuery = `SELECT * FROM ${nodesTable} WHERE id = ${nodeId}`
@@ -122,7 +122,7 @@ exports.getOnlineNodes = (callback) => {
// Call back the first (and theoretically only) row
// Specify 0 so downstream functions don't have to worry about it
return (callback) ? callback(returnNodeObjectFromRow(sqlResponse[0])) : returnNodeObjectFromRow(sqlResponse[0]);
return (callback) ? callback(await returnNodeObjectFromRow(sqlResponse[0])) : await returnNodeObjectFromRow(sqlResponse[0]);
}
exports.getNodeInfoFromId = getNodeInfoFromId
@@ -136,10 +136,9 @@ exports.addNewNode = async (nodeObject, callback) => {
ip = nodeObject.ip,
port = nodeObject.port,
location = nodeObject.location,
nearbySystems = utils.JsonToBuffer(nodeObject.nearbySystems),
online = nodeObject.online,
connected = 0;
const sqlQuery = `INSERT INTO ${nodesTable} (name, ip, port, location, nearbySystems, online, connected) VALUES ('${name}', '${ip}', ${port}, '${location}', '${nearbySystems}', ${online}, ${connected})`;
nearbySystems = utils.JsonToBuffer(nodeObject.nearbySystems ?? {}),
online = nodeObject.online
const sqlQuery = `INSERT INTO ${nodesTable} (name, ip, port, location, nearbySystems, online) VALUES ('${name}', '${ip}', ${port}, '${location}', '${nearbySystems}', ${online})`;
const sqlResponse = await new Promise((recordResolve, recordReject) => {
runSQL(sqlQuery, (rows) => {
@@ -149,7 +148,9 @@ exports.addNewNode = async (nodeObject, callback) => {
// Call back the first (and theoretically only) row
// Specify 0 so downstream functions don't have to worry about it
return (callback) ? callback(returnNodeObjectFromRow(sqlResponse)) : returnNodeObjectFromRow(sqlResponse);
const newNode = await this.getNodeInfoFromId(sqlResponse.insertId);
log.DEBUG("Added new node: ", newNode)
return (callback) ? callback(newNode) : newNode;
}
/** Update the known info on a node
@@ -206,7 +207,7 @@ exports.updateNodeInfo = async (nodeObject, callback = undefined) => {
});
if (sqlResponse.affectedRows === 1) return (callback) ? callback(true) : true;
else return (callback) ? callback(returnNodeObjectFromRows(sqlResponse)) : returnNodeObjectFromRows(sqlResponse);
else return (callback) ? callback(await returnNodeObjectFromRows(sqlResponse)) : await returnNodeObjectFromRows(sqlResponse);
}
/**
@@ -353,12 +354,12 @@ exports.getAllConnections = async (callback = undefined) => {
// Function to run and handle SQL errors
function runSQL(sqlQuery, callback = undefined, error = (err) => {
console.log(err);
log.ERROR(err);
throw err;
}) {
connection.query(sqlQuery, (err, rows) => {
if (err) return error(err);
//console.log('The rows are:', rows);
log.VERBOSE('Response for query: ', sqlQuery, rows);
return (callback) ? callback(rows) : rows
})
}

View File

@@ -110,8 +110,6 @@ class nodeObject {
* @param {*} param0._port The port that the client is listening on
* @param {*} param0._location The physical location of the node
* @param {*} param0._online True/False if the node is online or offline
* @param {*} param0._connected True/False if the bot is connected to discord or not
* @param {*} param0._connection The connection Object associated with the node, null if not checked, undefined if none exists
* @param {*} param0._nearbySystems An object array of nearby systems
*/
constructor({ _id = null, _name = null, _ip = null, _port = null, _location = null, _nearbySystems = null, _online = null }) {

View File

@@ -3,6 +3,7 @@ const { DebugBuilder } = require("../utilities/debugBuilder");
const { clientObject } = require("./recordHelper");
const { readFileSync } = require('fs');
const log = new DebugBuilder("server", "utils");
const logAC = new DebugBuilder("server", "command-autocorrect");
const path = require('path');
// Convert a JSON object to a buffer for the DB
@@ -117,3 +118,19 @@ exports.getClientObjectByClientID = (clientId) => {
}
return undefined
}
exports.filterAutocompleteValues = async (interaction, options) => {
// Get the command used
const command = interaction.command;
// Find values that start with what the user is entering
const focusedValue = interaction.options.getFocused();
const filtered = options.filter(preset => preset.startsWith(focusedValue));
// Give the query response to the user
logAC.DEBUG("Focused Value: ", command, focusedValue, options, filtered);
await interaction.respond(
filtered.map(option => ({ name: option, value: option })),
);
}