Merge pull request 'Add join command to server #7' (#15) from Add-join-command-to-server-#7 into master
Reviewed-on: #15
This commit is contained in:
1
Server/.gitignore
vendored
1
Server/.gitignore
vendored
@@ -4,3 +4,4 @@ package-lock.json
|
|||||||
*.bak
|
*.bak
|
||||||
*.log
|
*.log
|
||||||
*._.*
|
*._.*
|
||||||
|
clientIds.json
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
## TODOs
|
|
||||||
- ~~Create balance command for user to view their balance~~
|
|
||||||
- ~~Create a command that explains the pricing~~
|
|
||||||
- ~~Update welcome and insufficient replies ~~
|
|
||||||
- ~~add a section for the help menu to show items that need tokens~~
|
|
||||||
- add a limiter to the rss feeds to slow down the sends when multiple updates are occurring
|
|
||||||
- add a blank .env file to the git
|
|
||||||
- clean up logging
|
|
||||||
- ensure documentation for functions
|
|
||||||
- merge with Discord CnC
|
|
||||||
6
Server/clientIds.json.EXAMPLE
Normal file
6
Server/clientIds.json.EXAMPLE
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"[ID from Discord]": {
|
||||||
|
"name": "[Nickname of the Bot]",
|
||||||
|
"id": "[Client ID from Discord Dev Portal]"
|
||||||
|
}
|
||||||
|
}
|
||||||
116
Server/commands/join.js
Normal file
116
Server/commands/join.js
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
// Modules
|
||||||
|
const { customSlashCommandBuilder } = require('../utilities/customSlashCommandBuilder');
|
||||||
|
const { DebugBuilder } = require("../utilities/debugBuilder");
|
||||||
|
const { BufferToJson, getMembersInRole, getKeyByArrayValue } = require("../utilities/utils");
|
||||||
|
const { requestOptions, sendHttpRequest } = require("../utilities/httpRequests");
|
||||||
|
const { readFileSync } = require('fs');
|
||||||
|
const { getOnlineNodes, getNodeInfoFromId, updateNodeInfo } = require("../utilities/mysqlHandler");
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
// Global Vars
|
||||||
|
const log = new DebugBuilder("server", "join");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* * This wrapper will check if there is an available node with the requested preset and if so checks for an available client ID to join with
|
||||||
|
*
|
||||||
|
* @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
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
async function joinServerWrapper(presetName, channelId, clientIdsUsed) {
|
||||||
|
// Get nodes online
|
||||||
|
var onlineNodes = await new Promise((recordResolve, recordReject) => {
|
||||||
|
getOnlineNodes((nodeRows) => {
|
||||||
|
recordResolve(nodeRows);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
// Check which nodes have the selected preset
|
||||||
|
onlineNodes = onlineNodes.filter(node => node.nearbySystems.includes(presetName));
|
||||||
|
log.DEBUG("Filtered Online Nodes: ", onlineNodes);
|
||||||
|
// Check if any nodes with this preset are available
|
||||||
|
var nodesCurrentlyAvailable = [];
|
||||||
|
for (const node of onlineNodes) {
|
||||||
|
const reqOptions = new requestOptions("/bot/status", "GET", node.ip, node.port);
|
||||||
|
await new Promise(resolve => sendHttpRequest(reqOptions, "", (responseObj) => {
|
||||||
|
if (!responseObj || !responseObj.statusCode == 200) return resolve(false);
|
||||||
|
log.VERBOSE("Response Object from node ", node, responseObj);
|
||||||
|
nodesCurrentlyAvailable.push(node);
|
||||||
|
resolve(true);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
log.DEBUG("Nodes Currently Available: ", nodesCurrentlyAvailable);
|
||||||
|
// If not, let the user know
|
||||||
|
if (!nodesCurrentlyAvailable.length > 0) return Error("All nodes with this channel are unavailable, consider swapping one of the currently joined bots.");
|
||||||
|
|
||||||
|
// If so, join with the first node
|
||||||
|
var availableClientIds = await Object(JSON.parse(readFileSync(path.resolve(__dirname, '../clientIds.json'))));
|
||||||
|
log.DEBUG("All clients: ", Object.keys(availableClientIds));
|
||||||
|
|
||||||
|
var selectedClientId;
|
||||||
|
if (typeof clientIdsUsed === 'string') {
|
||||||
|
if (Object.keys(availableClientIds).includes(clientIdsUsed)) selectedClientId = availableClientIds[clientIdsUsed];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
log.DEBUG("Client IDs Used: ", clientIdsUsed.keys());
|
||||||
|
for (const usedClientId of clientIdsUsed.keys()) {
|
||||||
|
log.DEBUG("Used Client ID: ", usedClientId);
|
||||||
|
if (Object.keys(availableClientIds).includes(usedClientId)) {
|
||||||
|
delete availableClientIds[usedClientId];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.DEBUG("Available Client IDs: ", availableClientIds);
|
||||||
|
|
||||||
|
if (!Object.keys(availableClientIds).length > 0) return log.ERROR("All client ID have been used, consider swapping one of the curretly joined bots or adding more Client IDs to the pool.")
|
||||||
|
selectedClientId = availableClientIds[Object.keys(availableClientIds)[0]];
|
||||||
|
}
|
||||||
|
|
||||||
|
const selectedNode = nodesCurrentlyAvailable[0];
|
||||||
|
|
||||||
|
const reqOptions = new requestOptions("/bot/join", "POST", selectedNode.ip, selectedNode.port);
|
||||||
|
sendHttpRequest(reqOptions, JSON.stringify({
|
||||||
|
"channelId": channelId,
|
||||||
|
"clientId": selectedClientId.id,
|
||||||
|
"presetName": presetName
|
||||||
|
}), async (responseObj) => {
|
||||||
|
log.VERBOSE("Response Object from node ", selectedNode, responseObj);
|
||||||
|
if (!responseObj || !responseObj.statusCode == 200) return false;
|
||||||
|
// Node has connected to discord
|
||||||
|
selectedNode.connected = true;
|
||||||
|
const updatedNode = await updateNodeInfo(selectedNode)
|
||||||
|
log.DEBUG("Updated Node: ", updatedNode);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
exports.joinServerWrapper = joinServerWrapper;
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
data: new customSlashCommandBuilder()
|
||||||
|
.setName('join')
|
||||||
|
.setDescription('Join the channel you are in with the preset you choose')
|
||||||
|
.addAllSystemPresetOptions(),
|
||||||
|
example: "join",
|
||||||
|
isPrivileged: false,
|
||||||
|
requiresTokens: false,
|
||||||
|
defaultTokenUsage: 0,
|
||||||
|
deferInitialReply: true,
|
||||||
|
async execute(interaction) {
|
||||||
|
try{
|
||||||
|
const guildId = interaction.guild.id;
|
||||||
|
const presetName = interaction.options.getString('preset');
|
||||||
|
const channelId = interaction.member.voice.channel.id;
|
||||||
|
log.DEBUG(`Join requested by: ${interaction.user.username}, to: '${presetName}', in channel: ${channelId} / ${guildId}`);
|
||||||
|
await interaction.editReply('**Pong.**');
|
||||||
|
|
||||||
|
const onlineBots = await getMembersInRole(interaction);
|
||||||
|
|
||||||
|
log.DEBUG("Online Bots: ", onlineBots);
|
||||||
|
|
||||||
|
await joinServerWrapper(presetName, channelId, onlineBots.online);
|
||||||
|
//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)
|
||||||
|
//await interaction.reply(err.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,6 +6,7 @@ const {getAllNodes, addNewNode, updateNodeInfo, getNodeInfoFromId, getOnlineNode
|
|||||||
const utils = require("../utilities/utils");
|
const utils = require("../utilities/utils");
|
||||||
const { sendHttpRequest, requestOptions } = require("../utilities/httpRequests.js");
|
const { sendHttpRequest, requestOptions } = require("../utilities/httpRequests.js");
|
||||||
const { nodeObject } = require("../utilities/recordHelper.js");
|
const { nodeObject } = require("../utilities/recordHelper.js");
|
||||||
|
const { joinServerWrapper } = require("../commands/join");
|
||||||
|
|
||||||
const refreshInterval = process.env.NODE_MONITOR_REFRESH_INTERVAL ?? 1200000;
|
const refreshInterval = process.env.NODE_MONITOR_REFRESH_INTERVAL ?? 1200000;
|
||||||
|
|
||||||
@@ -90,6 +91,21 @@ exports.nodeCheckIn = async (req, res) => {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request the node to join the specified server/channel and listen to the specified resource
|
||||||
|
*
|
||||||
|
* @param req.body.clientId The client ID to join discord with (NOT dev portal ID, right click user -> Copy ID)
|
||||||
|
* @param req.body.channelId The Channel ID to join in Discord
|
||||||
|
* @param req.body.presetName The preset name to listen to in Discord
|
||||||
|
*/
|
||||||
|
exports.requestNodeJoinServer = async (req, res) => {
|
||||||
|
if (!req.body.clientId || !req.body.channelId || !req.body.presetName) return res.status(400).json("Missing information in request, requires clientId, channelId, presetName");
|
||||||
|
await joinServerWrapper(req.body.presetName, req.body.channelId, req.body.clientId)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The node monitor service, this will periodically check in on the online nodes to make sure they are still online
|
||||||
|
*/
|
||||||
exports.nodeMonitorService = class nodeMonitorService {
|
exports.nodeMonitorService = class nodeMonitorService {
|
||||||
constructor() {
|
constructor() {
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ const {
|
|||||||
} = require('discord.js');
|
} = require('discord.js');
|
||||||
|
|
||||||
const client = new Client({
|
const client = new Client({
|
||||||
intents: [GatewayIntentBits.GuildMessages, GatewayIntentBits.Guilds]
|
intents: [GatewayIntentBits.GuildMessages, GatewayIntentBits.Guilds, GatewayIntentBits.GuildPresences, GatewayIntentBits.GuildMembers]
|
||||||
});
|
});
|
||||||
|
|
||||||
prefix = process.env.PREFIX
|
prefix = process.env.PREFIX
|
||||||
@@ -118,10 +118,21 @@ const commandFiles = fs.readdirSync(commandsPath).filter(file => file.endsWith('
|
|||||||
for (const file of commandFiles) {
|
for (const file of commandFiles) {
|
||||||
const filePath = path.join(commandsPath, file);
|
const filePath = path.join(commandsPath, file);
|
||||||
const command = require(filePath);
|
const command = require(filePath);
|
||||||
log.DEBUG("Importing command: ", command.data.name);
|
if (command.data instanceof Promise) {
|
||||||
|
command.data.then(async (builder) => {
|
||||||
|
command.data = builder;
|
||||||
|
log.DEBUG("Importing command: ", command.data.name, command);
|
||||||
// Set a new item in the Collection
|
// Set a new item in the Collection
|
||||||
// With the key as the command name and the value as the exported module
|
// With the key as the command name and the value as the exported module
|
||||||
client.commands.set(command.data.name, command);
|
client.commands.set(command.data.name, command);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
log.DEBUG("Importing command: ", command.data.name, command);
|
||||||
|
// Set a new item in the Collection
|
||||||
|
// With the key as the command name and the value as the exported module
|
||||||
|
client.commands.set(command.data.name, command);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run when the bot is ready
|
// Run when the bot is ready
|
||||||
@@ -137,7 +148,7 @@ client.on('ready', () => {
|
|||||||
runHTTPServer();
|
runHTTPServer();
|
||||||
|
|
||||||
log.DEBUG("Starting Node Monitoring Service");
|
log.DEBUG("Starting Node Monitoring Service");
|
||||||
runNodeMonitorService();
|
//runNodeMonitorService();
|
||||||
|
|
||||||
log.DEBUG("Starting RSS watcher");
|
log.DEBUG("Starting RSS watcher");
|
||||||
runRssService();
|
runRssService();
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ const log = new DebugBuilder("server", "libUtils");
|
|||||||
const { NodeHtmlMarkdown } = require('node-html-markdown');
|
const { NodeHtmlMarkdown } = require('node-html-markdown');
|
||||||
const { parse } = require("node-html-parser");
|
const { parse } = require("node-html-parser");
|
||||||
const crypto = require("crypto");
|
const crypto = require("crypto");
|
||||||
|
require('dotenv').config();
|
||||||
|
|
||||||
const imageRegex = /(http(s?):)([/|.|\w|\s|-])*((\.(?:jpg|gif|png|webm))|(\/gallery\/(?:[/|.|\w|\s|-])*))/g;
|
const imageRegex = /(http(s?):)([/|.|\w|\s|-])*((\.(?:jpg|gif|png|webm))|(\/gallery\/(?:[/|.|\w|\s|-])*))/g;
|
||||||
const youtubeVideoRegex = /((?:https?:)?\/\/)?((?:www|m)\.)?((?:youtube(-nocookie)?\.com|youtu.be))(\/(?:[\w\-]+\?v=|embed\/|v\/)?)([\w\-]+)/g
|
const youtubeVideoRegex = /((?:https?:)?\/\/)?((?:www|m)\.)?((?:youtube(-nocookie)?\.com|youtu.be))(\/(?:[\w\-]+\?v=|embed\/|v\/)?)([\w\-]+)/g
|
||||||
@@ -55,6 +56,7 @@ exports.onError = (error) => {
|
|||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var port = process.env.HTTP_PORT;
|
||||||
var bind = typeof port === 'string'
|
var bind = typeof port === 'string'
|
||||||
? 'Pipe ' + port
|
? 'Pipe ' + port
|
||||||
: 'Port ' + port;
|
: 'Port ' + port;
|
||||||
|
|||||||
1709
Server/package-lock.json
generated
1709
Server/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -4,30 +4,30 @@
|
|||||||
"description": "Discord RSS News Bot",
|
"description": "Discord RSS News Bot",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@discordjs/builders": "~1.4.0",
|
"@discordjs/builders": "^1.6.3",
|
||||||
"@discordjs/rest": "~1.5.0",
|
"@discordjs/rest": "^1.7.1",
|
||||||
"axios": "~1.3.4",
|
"axios": "^1.4.0",
|
||||||
"chatgpt": "~4.7.2",
|
"chatgpt": "^5.2.4",
|
||||||
"cookie-parser": "~1.4.4",
|
"cookie-parser": "^1.4.6",
|
||||||
"debug": "~2.6.9",
|
"debug": "^4.3.4",
|
||||||
"discord-api-types": "~0.37.35",
|
"discord-api-types": "^0.37.42",
|
||||||
"discord.js": "~14.7.1",
|
"discord.js": "^14.11.0",
|
||||||
"dotenv": "~16.0.3",
|
"dotenv": "^16.0.3",
|
||||||
"ejs": "~2.6.1",
|
"ejs": "^3.1.9",
|
||||||
"express": "~4.18.2",
|
"express": "^4.18.2",
|
||||||
"fs": "~0.0.1-security",
|
"fs": "^0.0.1-security",
|
||||||
"gpt-3-encoder": "~1.1.4",
|
"gpt-3-encoder": "^1.1.4",
|
||||||
"http-errors": "~1.6.3",
|
"http-errors": "*",
|
||||||
"jsdoc": "~3.6.7",
|
"jsdoc": "^4.0.2",
|
||||||
"jsonfile": "~6.1.0",
|
"jsonfile": "^6.1.0",
|
||||||
"morgan": "~1.9.1",
|
"morgan": "^1.10.0",
|
||||||
"mysql": "~2.18.1",
|
"mysql": "^2.18.1",
|
||||||
"node-html-markdown": "~1.3.0",
|
"node-html-markdown": "^1.3.0",
|
||||||
"node-html-parser": "~6.1.5",
|
"node-html-parser": "^6.1.5",
|
||||||
"openai": "~3.1.0",
|
"openai": "^3.2.1",
|
||||||
"parse-files": "~0.1.1",
|
"parse-files": "^0.1.1",
|
||||||
"rss-parser": "~3.12.0",
|
"rss-parser": "^3.13.0",
|
||||||
"user-agents": "~1.0.1303"
|
"user-agents": "^1.0.1393"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "echo \"Error: no test specified\" && exit 1",
|
"test": "echo \"Error: no test specified\" && exit 1",
|
||||||
|
|||||||
@@ -28,4 +28,8 @@ router.get('/nodeInfo', nodesController.getNodeInfo);
|
|||||||
// Client checkin with the server to update information
|
// Client checkin with the server to update information
|
||||||
router.post('/nodeCheckIn', nodesController.nodeCheckIn);
|
router.post('/nodeCheckIn', nodesController.nodeCheckIn);
|
||||||
|
|
||||||
|
// TODO Need to authenticate this request
|
||||||
|
// Request a particular client to join a particular channel listening to a particular preset
|
||||||
|
router.post('/joinServer', nodesController.requestNodeJoinServer);
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
||||||
|
|||||||
46
Server/utilities/customSlashCommandBuilder.js
Normal file
46
Server/utilities/customSlashCommandBuilder.js
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@@ -22,6 +22,7 @@ exports.deploy = (clientId, guildIDs) => {
|
|||||||
// Grab the SlashCommandBuilder#toJSON() output of each command's data for deployment
|
// Grab the SlashCommandBuilder#toJSON() output of each command's data for deployment
|
||||||
for (const file of commandFiles) {
|
for (const file of commandFiles) {
|
||||||
const command = require(`${path.resolve(commandsPath, file)}`);
|
const command = require(`${path.resolve(commandsPath, file)}`);
|
||||||
|
log.VERBOSE('Deploying Command: ', command);
|
||||||
commands.push(command.data.toJSON());
|
commands.push(command.data.toJSON());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ const { DebugBuilder } = require("../utilities/debugBuilder.js");
|
|||||||
const log = new DebugBuilder("server", "httpRequests");
|
const log = new DebugBuilder("server", "httpRequests");
|
||||||
// Modules
|
// Modules
|
||||||
const http = require("http");
|
const http = require("http");
|
||||||
|
const { isJsonString } = require("./utils.js");
|
||||||
|
|
||||||
exports.requestOptions = class requestOptions {
|
exports.requestOptions = class requestOptions {
|
||||||
/**
|
/**
|
||||||
@@ -39,7 +40,7 @@ exports.sendHttpRequest = function sendHttpRequest(requestOptions, data, callbac
|
|||||||
res.on('data', (data) => {
|
res.on('data', (data) => {
|
||||||
const responseObject = {
|
const responseObject = {
|
||||||
"statusCode": res.statusCode,
|
"statusCode": res.statusCode,
|
||||||
"body": (requestOptions.method === "POST") ? JSON.parse(data) : data.toString()
|
"body": (isJsonString(data.toString)) ? JSON.parse(data) : data.toString()
|
||||||
};
|
};
|
||||||
log.DEBUG("Response Object: ", responseObject);
|
log.DEBUG("Response Object: ", responseObject);
|
||||||
callback(responseObject);
|
callback(responseObject);
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
require('dotenv').config();
|
require('dotenv').config();
|
||||||
const mysql = require('mysql');
|
const mysql = require('mysql');
|
||||||
const utils = require('./utils');
|
const utils = require('./utils');
|
||||||
|
const { nodeObject } = require("./recordHelper");
|
||||||
|
const { DebugBuilder } = require("../utilities/debugBuilder");
|
||||||
|
const { BufferToJson } = require("../utilities/utils");
|
||||||
|
|
||||||
|
const log = new DebugBuilder("server", "mysSQLHandler");
|
||||||
|
|
||||||
const connection = mysql.createPool({
|
const connection = mysql.createPool({
|
||||||
host: process.env.NODE_DB_HOST,
|
host: process.env.NODE_DB_HOST,
|
||||||
@@ -11,23 +16,77 @@ const connection = mysql.createPool({
|
|||||||
|
|
||||||
const nodesTable = `${process.env.NODE_DB_NAME}.nodes`;
|
const nodesTable = `${process.env.NODE_DB_NAME}.nodes`;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a node object from a single SQL row
|
||||||
|
*
|
||||||
|
* @param {object} row The row to convert to a node object
|
||||||
|
* @returns {nodeObject} The converted node object to be used downstream
|
||||||
|
*/
|
||||||
|
function returnNodeObjectFromRow(row) {
|
||||||
|
return new nodeObject({
|
||||||
|
_id: row.id,
|
||||||
|
_name: row.name,
|
||||||
|
_ip: row.ip,
|
||||||
|
_port: row.port,
|
||||||
|
_location: row.location,
|
||||||
|
_nearbySystems: BufferToJson(row.nearbySystems),
|
||||||
|
_online: (row.online === 1) ? true : false,
|
||||||
|
_connected: (row.connected === 1) ? true : false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrapper to convert an array of rows to an array of nodeObjects
|
||||||
|
*
|
||||||
|
* @param {array} rows The array of SQL results to be converted into node objects
|
||||||
|
* @returns {array} An array of node objects
|
||||||
|
*/
|
||||||
|
function returnNodeObjectFromRows(rows) {
|
||||||
|
var i = 0;
|
||||||
|
for (var row of rows){
|
||||||
|
log.DEBUG("Row: ", row);
|
||||||
|
rows[i] = returnNodeObjectFromRow(row);
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
log.DEBUG("Converted Objects from Rows: ", rows);
|
||||||
|
return rows;
|
||||||
|
}
|
||||||
|
|
||||||
/** Get all nodes the server knows about regardless of status
|
/** Get all nodes the server knows about regardless of status
|
||||||
* @param {*} callback Callback function
|
* @param {*} callback Callback function
|
||||||
*/
|
*/
|
||||||
exports.getAllNodes = (callback) => {
|
exports.getAllNodes = (callback) => {
|
||||||
const sqlQuery = `SELECT * FROM ${nodesTable}`
|
const sqlQuery = `SELECT * FROM ${nodesTable}`
|
||||||
runSQL(sqlQuery, (rows) => {
|
runSQL(sqlQuery, (rows) => {
|
||||||
return callback(rows);
|
if(!rows || rows.length == 0) callback(undefined);
|
||||||
|
|
||||||
|
return callback(returnNodeObjectFromRows(rows));
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all Nodes synchronously **May not be working**
|
||||||
|
*
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
exports.getAllNodesSync = async () => {
|
||||||
|
const sqlQuery = `SELECT * FROM ${nodesTable}`
|
||||||
|
var returnObjects = [];
|
||||||
|
const rows = await runSQL(sqlQuery);
|
||||||
|
|
||||||
|
console.log("Rows: ", rows);
|
||||||
|
|
||||||
|
return returnNodeObjectFromRows(rows);
|
||||||
|
}
|
||||||
|
|
||||||
/** Get all nodes that have the online status set true (are online)
|
/** Get all nodes that have the online status set true (are online)
|
||||||
* @param callback Callback function
|
* @param callback Callback function
|
||||||
*/
|
*/
|
||||||
exports.getOnlineNodes = (callback) => {
|
exports.getOnlineNodes = (callback) => {
|
||||||
const sqlQuery = `SELECT * FROM ${nodesTable} WHERE online = 1;`
|
const sqlQuery = `SELECT * FROM ${nodesTable} WHERE online = 1;`
|
||||||
runSQL(sqlQuery, (rows) => {
|
runSQL(sqlQuery, (rows) => {
|
||||||
return callback(rows);
|
return callback(returnNodeObjectFromRows(rows));
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,7 +99,7 @@ exports.getNodeInfoFromId = (nodeId, callback) => {
|
|||||||
runSQL(sqlQuery, (rows) => {
|
runSQL(sqlQuery, (rows) => {
|
||||||
// Call back the first (and theoretically only) row
|
// Call back the first (and theoretically only) row
|
||||||
// Specify 0 so downstream functions don't have to worry about it
|
// Specify 0 so downstream functions don't have to worry about it
|
||||||
return callback(rows[0]);
|
return callback(returnNodeObjectFromRow(rows[0]));
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,11 +114,12 @@ exports.addNewNode = (nodeObject, callback) => {
|
|||||||
port = nodeObject.port,
|
port = nodeObject.port,
|
||||||
location = nodeObject.location,
|
location = nodeObject.location,
|
||||||
nearbySystems = utils.JsonToBuffer(nodeObject.nearbySystems),
|
nearbySystems = utils.JsonToBuffer(nodeObject.nearbySystems),
|
||||||
online = nodeObject.online;
|
online = nodeObject.online,
|
||||||
const sqlQuery = `INSERT INTO ${nodesTable} (name, ip, port, location, nearbySystems, online) VALUES ('${name}', '${ip}', ${port}, '${location}', '${nearbySystems}', ${online})`;
|
connected = 0;
|
||||||
|
const sqlQuery = `INSERT INTO ${nodesTable} (name, ip, port, location, nearbySystems, online, connected) VALUES ('${name}', '${ip}', ${port}, '${location}', '${nearbySystems}', ${online}, ${connected})`;
|
||||||
|
|
||||||
runSQL(sqlQuery, (rows) => {
|
runSQL(sqlQuery, (rows) => {
|
||||||
return callback(rows);
|
return callback(returnNodeObjectFromRows(rows));
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,13 +127,14 @@ exports.addNewNode = (nodeObject, callback) => {
|
|||||||
* @param nodeObject Node information object
|
* @param nodeObject Node information object
|
||||||
* @param callback Callback function
|
* @param callback Callback function
|
||||||
*/
|
*/
|
||||||
exports.updateNodeInfo = (nodeObject, callback) => {
|
exports.updateNodeInfo = (nodeObject, callback = undefined) => {
|
||||||
if(!nodeObject.id) throw new Error("Attempted to updated node without providing ID", nodeObject);
|
if(!nodeObject.id) throw new Error("Attempted to updated node without providing ID", nodeObject);
|
||||||
const name = nodeObject.name,
|
const name = nodeObject.name,
|
||||||
ip = nodeObject.ip,
|
ip = nodeObject.ip,
|
||||||
port = nodeObject.port,
|
port = nodeObject.port,
|
||||||
location = nodeObject.location,
|
location = nodeObject.location,
|
||||||
online = nodeObject.online;
|
online = nodeObject.online,
|
||||||
|
connected = nodeObject.connected;
|
||||||
let queryParams = [],
|
let queryParams = [],
|
||||||
nearbySystems = nodeObject.nearbySystems;
|
nearbySystems = nodeObject.nearbySystems;
|
||||||
|
|
||||||
@@ -89,9 +150,13 @@ exports.updateNodeInfo = (nodeObject, callback) => {
|
|||||||
if (online || online === 1) queryParams.push(`online = 1`);
|
if (online || online === 1) queryParams.push(`online = 1`);
|
||||||
else queryParams.push(`online = 0`);
|
else queryParams.push(`online = 0`);
|
||||||
}
|
}
|
||||||
|
if (typeof connected === "boolean" || typeof connected === "number") {
|
||||||
|
if (online || online === 1) queryParams.push(`connected = 1`);
|
||||||
|
else queryParams.push(`connected = 0`);
|
||||||
|
}
|
||||||
|
|
||||||
let sqlQuery = `UPDATE ${nodesTable} SET`
|
let sqlQuery = `UPDATE ${nodesTable} SET`
|
||||||
if (!queryParams || queryParams.length === 0) return callback(undefined);
|
if (!queryParams || queryParams.length === 0) return (callback) ? callback(undefined) : undefined;
|
||||||
if (queryParams.length === 1) {
|
if (queryParams.length === 1) {
|
||||||
sqlQuery = `${sqlQuery} ${queryParams[0]}`
|
sqlQuery = `${sqlQuery} ${queryParams[0]}`
|
||||||
} else {
|
} else {
|
||||||
@@ -111,8 +176,8 @@ exports.updateNodeInfo = (nodeObject, callback) => {
|
|||||||
sqlQuery = `${sqlQuery} WHERE id = ${nodeObject.id};`
|
sqlQuery = `${sqlQuery} WHERE id = ${nodeObject.id};`
|
||||||
|
|
||||||
runSQL(sqlQuery, (rows) => {
|
runSQL(sqlQuery, (rows) => {
|
||||||
if (rows.affectedRows === 1) return callback(true);
|
if (rows.affectedRows === 1) return (callback) ? callback(true) : true;
|
||||||
else return callback(rows);
|
else return (callback) ? callback(returnNodeObjectFromRows(rows)) : returnNodeObjectFromRows(rows);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,7 +186,7 @@ function runSQL(sqlQuery, callback, error = (err) => {
|
|||||||
console.log(err);
|
console.log(err);
|
||||||
throw err;
|
throw err;
|
||||||
}) {
|
}) {
|
||||||
connection.query(sqlQuery, (err, rows) => {
|
return connection.query(sqlQuery, (err, rows) => {
|
||||||
if (err) return error(err);
|
if (err) return error(err);
|
||||||
//console.log('The rows are:', rows);
|
//console.log('The rows are:', rows);
|
||||||
return callback(rows);
|
return callback(rows);
|
||||||
|
|||||||
@@ -109,10 +109,11 @@ class nodeObject {
|
|||||||
* @param {*} param0._ip The IP that the master can contact the node at
|
* @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._port The port that the client is listening on
|
||||||
* @param {*} param0._location The physical location of the node
|
* @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._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._nearbySystems An object array of nearby systems
|
* @param {*} param0._nearbySystems An object array of nearby systems
|
||||||
*/
|
*/
|
||||||
constructor({ _id = null, _name = null, _ip = null, _port = null, _location = null, _nearbySystems = null, _online = null }) {
|
constructor({ _id = null, _name = null, _ip = null, _port = null, _location = null, _nearbySystems = null, _online = null, _connected = null }) {
|
||||||
this.id = _id;
|
this.id = _id;
|
||||||
this.name = _name;
|
this.name = _name;
|
||||||
this.ip = _ip;
|
this.ip = _ip;
|
||||||
@@ -120,6 +121,7 @@ class nodeObject {
|
|||||||
this.location = _location;
|
this.location = _location;
|
||||||
this.nearbySystems = _nearbySystems;
|
this.nearbySystems = _nearbySystems;
|
||||||
this.online = _online;
|
this.online = _online;
|
||||||
|
this.connected = _connected;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,7 @@
|
|||||||
|
// Debug
|
||||||
|
const { DebugBuilder } = require("../utilities/debugBuilder");
|
||||||
|
const log = new DebugBuilder("server", "utils");
|
||||||
|
|
||||||
// Convert a JSON object to a buffer for the DB
|
// Convert a JSON object to a buffer for the DB
|
||||||
exports.JsonToBuffer = (jsonObject) => {
|
exports.JsonToBuffer = (jsonObject) => {
|
||||||
return Buffer.from(JSON.stringify(jsonObject))
|
return Buffer.from(JSON.stringify(jsonObject))
|
||||||
@@ -8,6 +12,43 @@ exports.BufferToJson = (buffer) => {
|
|||||||
return JSON.parse(buffer.toString());
|
return JSON.parse(buffer.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* **DISUSED**
|
||||||
|
*
|
||||||
|
* @param {string} presetName The present name to sanitize
|
||||||
|
* @returns {string} The sanitized preset name to be used elsewhere
|
||||||
|
*/
|
||||||
|
exports.SanitizePresetName = (presetName) => {
|
||||||
|
return String(presetName).toLowerCase().replace(/[\W_]+/g,"-")
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get online, offline and total members in a guild. Optionally a group can be specified to get members' statuses.
|
||||||
|
*
|
||||||
|
* @param interaction Discord interaction object
|
||||||
|
* @param param0.roleName {OPTIONAL} The role name to check the members in; Defaults to 'Bots'
|
||||||
|
*/
|
||||||
|
exports.getMembersInRole = async (interaction, roleName = "Bots" ) => {
|
||||||
|
log.DEBUG("Fetching all members");
|
||||||
|
await interaction.guild.members.fetch() //cache all members in the server
|
||||||
|
const role = await interaction.guild.roles.cache.find(role => role.name === roleName); //the role to check
|
||||||
|
log.DEBUG("Role to check members from: ", role);
|
||||||
|
log.DEBUG("Members of role: ", role.members);
|
||||||
|
|
||||||
|
// This is not working, can't get the status of the users, rest of join is untested
|
||||||
|
const onlineMembers = await role.members.filter(member => member.voice.channel !== null);
|
||||||
|
const offlineMembers = await role.members.filter(member => member.voice.channel === null);
|
||||||
|
const allMembers = await role.members;
|
||||||
|
|
||||||
|
log.VERBOSE("All members: ", allMembers, onlineMembers, offlineMembers)
|
||||||
|
|
||||||
|
return {
|
||||||
|
'online': onlineMembers,
|
||||||
|
'offline': offlineMembers,
|
||||||
|
'all': allMembers
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Find a key in an object by its value
|
/** Find a key in an object by its value
|
||||||
*
|
*
|
||||||
* @param {*} object The object to search
|
* @param {*} object The object to search
|
||||||
@@ -17,3 +58,18 @@ exports.BufferToJson = (buffer) => {
|
|||||||
exports.getKeyByArrayValue = (object, value) => {
|
exports.getKeyByArrayValue = (object, value) => {
|
||||||
return Object.keys(object).find(key => object[key].includes(value));
|
return Object.keys(object).find(key => object[key].includes(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check to see if the input is a valid JSON string
|
||||||
|
*
|
||||||
|
* @param {*} str The string to check for valud JSON
|
||||||
|
* @returns {true|false}
|
||||||
|
*/
|
||||||
|
exports.isJsonString = (str) => {
|
||||||
|
try {
|
||||||
|
JSON.parse(str);
|
||||||
|
} catch (e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user