Compare commits
41 Commits
feature/#1
...
851a9c55fa
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
851a9c55fa | ||
|
|
fb9df3230e | ||
|
|
7163df21e9 | ||
|
|
47d18c494d | ||
|
|
ea2dbd9fb0 | ||
|
|
9ce77a5be0 | ||
|
|
57881a7e17 | ||
|
|
e350cd6030 | ||
|
|
fba0a2a2b2 | ||
|
|
83cef63109 | ||
|
|
2390bdc2c6 | ||
|
|
93be4ca9dc | ||
|
|
d96e6ad519 | ||
|
|
b180f90973 | ||
|
|
fd7435c7bc | ||
|
|
e062cf5794 | ||
|
|
597546b73d | ||
|
|
333e7420f4 | ||
|
|
37a03c5cc6 | ||
|
|
d2e9f286e2 | ||
|
|
255b1282ec | ||
|
|
878e64fa42 | ||
|
|
7a040a8e97 | ||
|
|
8dffeccf83 | ||
|
|
2108a3b92b | ||
|
|
960b801dd2 | ||
|
|
dd5b442377 | ||
|
|
c5a7131063 | ||
|
|
5d54f07af4 | ||
|
|
24faa5279d | ||
|
|
79d2ca1887 | ||
|
|
c2b4b4bfa1 | ||
|
|
d8a697e583 | ||
|
|
44caa11f7c | ||
|
|
dc92b07426 | ||
|
|
92f4caad0c | ||
|
|
b888a9233d | ||
|
|
b4e27162aa | ||
|
|
bfda15866e | ||
|
|
f4475dc9d7 | ||
|
|
c4650a9e99 |
@@ -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);
|
||||
|
||||
@@ -5,7 +5,7 @@ const log = new DebugBuilder("client", "clientController");
|
||||
require('dotenv').config();
|
||||
const modes = require("../config/modes");
|
||||
// Modules
|
||||
const { executeAsyncConsoleCommand, nodeObject } = require("../utilities/utilities");
|
||||
const { executeAsyncConsoleCommand, nodeObject, BufferToJson } = require("../utilities/utilities");
|
||||
// Utilities
|
||||
const { updateId, updateConfig } = require("../utilities/updateConfig");
|
||||
const { updatePreset, addNewPreset, getPresets, removePreset } = require("../utilities/updatePresets");
|
||||
@@ -67,22 +67,27 @@ 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') {
|
||||
await updateId(0);
|
||||
runningClientConfig.id = 0;
|
||||
}
|
||||
|
||||
if (!runningClientConfig.ip) {
|
||||
const ipAddr = await checkLocalIP();
|
||||
updateConfig('ip', ipAddr);
|
||||
await updateConfig('CLIENT_IP', ipAddr);
|
||||
runningClientConfig.ip = ipAddr;
|
||||
}
|
||||
|
||||
if(!runningClientConfig.name) {
|
||||
const lastOctet = await String(checkLocalIP()).spit('.')[-1];
|
||||
const name = `Radio-Node-${lastOctet}`;
|
||||
updateConfig('name', name);
|
||||
await updateConfig('CLIENT_NAME', name);
|
||||
runningClientConfig.name = name;
|
||||
}
|
||||
|
||||
if(!runningClientConfig.port) {
|
||||
const port = 3010;
|
||||
updateConfig('port', port);
|
||||
await updateConfig('CLIENT_PORT', port);
|
||||
runningClientConfig.port = port;
|
||||
}
|
||||
|
||||
@@ -95,21 +100,34 @@ exports.checkConfig = async function checkConfig() {
|
||||
exports.checkIn = async () => {
|
||||
let reqOptions;
|
||||
await this.checkConfig();
|
||||
runningClientConfig.online = true;
|
||||
// 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) {
|
||||
try {
|
||||
if (!runningClientConfig?.id || 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) {
|
||||
sendHttpRequest(reqOptions, JSON.stringify(runningClientConfig), async (responseObject) => {
|
||||
// Check if the server responded
|
||||
if (!responseObject) {
|
||||
log.WARN("Server did not respond to checkIn. Will wait 60 seconds then try again");
|
||||
setTimeout(() => {
|
||||
// Run itself again to see if the server is up now
|
||||
this.checkIn();
|
||||
}, 60000);
|
||||
return
|
||||
}
|
||||
|
||||
// Update the client's ID if the server accepted its
|
||||
if (responseObject.statusCode === 202) {
|
||||
runningClientConfig.id = responseObject.body.nodeId;
|
||||
updateId(responseObject.body.nodeId);
|
||||
log.DEBUG("Response object from new node: ", responseObject, runningClientConfig);
|
||||
await updateId(runningClientConfig.id);
|
||||
}
|
||||
|
||||
if (responseObject.statusCode >= 300) {
|
||||
// Server threw an error
|
||||
onHttpError(responseObject.statusCode);
|
||||
log.DEBUG("HTTP Error: ", responseObject, await BufferToJson(responseObject.body));
|
||||
await onHttpError(responseObject.statusCode);
|
||||
}
|
||||
|
||||
});
|
||||
@@ -118,7 +136,19 @@ 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);
|
||||
// Check if the server responded
|
||||
if (!responseObject) {
|
||||
log.WARN("Server did not respond to checkIn. Will wait 60 seconds then try again");
|
||||
setTimeout(() => {
|
||||
// Run itself again to see if the server is up now
|
||||
this.checkIn();
|
||||
}, 60000);
|
||||
return
|
||||
}
|
||||
|
||||
if (responseObject.statusCode === 202) {
|
||||
log.DEBUG("Updated keys: ", responseObject.body.updatedKeys)
|
||||
// Server accepted an update
|
||||
}
|
||||
if (responseObject.statusCode === 200) {
|
||||
|
||||
1992
Client/package-lock.json
generated
1992
Client/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -17,7 +17,7 @@
|
||||
"express": "~4.16.1",
|
||||
"http-errors": "~1.6.3",
|
||||
"morgan": "~1.9.1",
|
||||
"replace-in-file": "~6.3.5",
|
||||
"replace-in-file": "~7.0.1",
|
||||
"@discordjs/builders": "^1.4.0",
|
||||
"@discordjs/rest": "^1.4.0",
|
||||
"discord.js": "^14.7.1"
|
||||
|
||||
@@ -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,14 +25,16 @@ 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)
|
||||
client = commands.Bot(command_prefix='!', intents=intents)
|
||||
|
||||
@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
|
||||
)
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -5,11 +5,16 @@ const log = new DebugBuilder("client", "httpRequests");
|
||||
require('dotenv').config();
|
||||
// Modules
|
||||
const http = require("http");
|
||||
const { isJsonString } = require("./utilities.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;
|
||||
log.VERBOSE("Hostname Vars: ", hostname, process.env.SERVER_HOSTNAME, process.env.SERVER_IP);
|
||||
if (hostname) this.hostname = hostname;
|
||||
if (process.env.SERVER_HOSTNAME) this.hostname = process.env.SERVER_HOSTNAME;
|
||||
if (process.env.SERVER_IP) this.hostname = process.env.SERVER_IP;
|
||||
if (!this.hostname) throw new Error("No server hostname / IP was given when creating a request");
|
||||
this.path = path;
|
||||
this.port = port ?? process.env.SERVER_PORT;
|
||||
this.method = method;
|
||||
@@ -31,31 +36,26 @@ exports.sendHttpRequest = function sendHttpRequest(requestOptions, data, callbac
|
||||
// Create the request
|
||||
const req = http.request(requestOptions, res => {
|
||||
res.on('data', (data) => {
|
||||
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);
|
||||
}
|
||||
const responseObject = {
|
||||
"statusCode": res.statusCode,
|
||||
"body": (isJsonString(data.toString())) ? JSON.parse(data.toString()) : data.toString()
|
||||
};
|
||||
log.VERBOSE("Response Object: ", responseObject);
|
||||
callback(responseObject);
|
||||
})
|
||||
}).on('error', err => {
|
||||
log.ERROR('Error: ', err.message)
|
||||
if (err.code === "ECONNREFUSED"){
|
||||
// Bot refused connection, assumed offline
|
||||
log.WARN("Connection Refused");
|
||||
}
|
||||
else log.ERROR('Error: ', err.message, err);
|
||||
callback(undefined);
|
||||
// TODO need to handle if the server is down
|
||||
})
|
||||
|
||||
// Write the data to the request and send it
|
||||
req.write(data)
|
||||
req.end()
|
||||
req.write(data);
|
||||
req.end();
|
||||
}
|
||||
|
||||
exports.onHttpError = function onHttpError(httpStatusCode) {
|
||||
|
||||
@@ -8,10 +8,10 @@ class Options {
|
||||
constructor(key, updatedValue) {
|
||||
this.files = "./.env";
|
||||
// A regex of the line containing the key in the config file
|
||||
this.from = new RegExp(`${key}="(.+)",`, "g");
|
||||
this.from = new RegExp(`${key}="?(.+)"?`, "g");
|
||||
// Check to see if the value is a string and needs to be wrapped in double quotes
|
||||
if (Array(["string", "number"]).includes(typeof updatedValue)) this.to = `${key}="${updatedValue}",`;
|
||||
else this.to = `${key}=${updatedValue},`;
|
||||
else this.to = `${key}=${updatedValue}`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ class Options {
|
||||
* @param updatedId The updated ID assigned to the bot
|
||||
*/
|
||||
exports.updateId = (updatedId) => {
|
||||
this.updateConfig('id', updatedId);
|
||||
this.updateConfig('CLIENT_ID', updatedId);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -46,7 +46,7 @@ exports.updateConfig = function updateConfig(key, value) {
|
||||
function updateConfigFile(options, callback){
|
||||
replace(options, (error, changedFiles) => {
|
||||
if (error) return console.error('Error occurred:', error);
|
||||
log.DEBUG('Modified files:', changedFiles);
|
||||
log.VERBOSE('Modified files:', changedFiles);
|
||||
callback(changedFiles);
|
||||
});
|
||||
}
|
||||
@@ -261,4 +261,24 @@ function convertRadioPresetsToOP25Config(presetName){
|
||||
|
||||
log.DEBUG(updatedOP25Config);
|
||||
return updatedOP25Config;
|
||||
}
|
||||
}
|
||||
|
||||
// Convert a buffer from the DB to JSON object
|
||||
exports.BufferToJson = (buffer) => {
|
||||
return JSON.parse(buffer.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
52
Server/commands/giveRole.js
Normal file
52
Server/commands/giveRole.js
Normal 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());
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -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) => {
|
||||
@@ -25,7 +25,7 @@ async function joinServerWrapper(presetName, channelId, clientIdsUsed) {
|
||||
});
|
||||
|
||||
// Check which nodes have the selected preset
|
||||
onlineNodes = onlineNodes.filter(node => node.nearbySystems.includes(presetName));
|
||||
onlineNodes = onlineNodes.filter(node => node.presets.includes(presetName));
|
||||
log.DEBUG("Filtered Online Nodes: ", onlineNodes);
|
||||
|
||||
// Check if any nodes with this preset are available
|
||||
@@ -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,33 +84,63 @@ 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);
|
||||
presetsAvailable.push.apply(presetsAvailable, nodeObject.presets);
|
||||
}
|
||||
|
||||
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;
|
||||
const presetName = interaction.options.getString('preset');
|
||||
if (!interaction.member.voice.channel.id) return interaction.editReply(`You need to be in a voice channel, ${interaction.user}`)
|
||||
if (!interaction.member.voice.channel) return interaction.editReply(`You need to be in a voice channel, ${interaction.user}`)
|
||||
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)
|
||||
|
||||
@@ -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,30 +33,26 @@ 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 =>
|
||||
option.setName("bot")
|
||||
.setDescription("The bot to disconnect from the server")
|
||||
.setAutocomplete(true)),
|
||||
.setAutocomplete(true)
|
||||
.setRequired(true)),
|
||||
example: "leave",
|
||||
isPrivileged: false,
|
||||
requiresTokens: false,
|
||||
defaultTokenUsage: 0,
|
||||
deferInitialReply: true,
|
||||
async autocomplete(interaction) {
|
||||
const focusedValue = interaction.options.getFocused();
|
||||
async autocomplete(interaction) {
|
||||
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;
|
||||
try{
|
||||
const botName = interaction.options.getString('bot');
|
||||
log.DEBUG("Bot Name: ", botName)
|
||||
const clinetIds = await getAllClientIds();
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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
|
||||
@@ -35,12 +35,12 @@ exports.newNode = async (req, res) => {
|
||||
_port: req.body.port ?? null,
|
||||
_location: req.body.location ?? null,
|
||||
_nearbySystems: req.body.nearbySystems ?? null,
|
||||
_online: req.body.online ?? 0
|
||||
_online: (req.body.online == "true" || req.body.online == "True") ? true : false
|
||||
});
|
||||
|
||||
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) {
|
||||
@@ -65,30 +65,62 @@ exports.getNodeInfo = async (req, res) => {
|
||||
exports.nodeCheckIn = async (req, res) => {
|
||||
if (!req.body.id) return res.status(400).json("No id specified");
|
||||
getNodeInfoFromId(req.body.id, (nodeInfo) => {
|
||||
let checkInObject = new nodeObject();
|
||||
// Convert the DB systems buffer to a JSON object to be worked with
|
||||
nodeInfo.nearbySystems = utils.BufferToJson(nodeInfo.nearbySystems)
|
||||
let checkInObject = {};
|
||||
// Convert the online status to a boolean to be worked with
|
||||
nodeInfo.online = nodeInfo.online !== 0;
|
||||
log.DEBUG("REQ Body: ", req.body);
|
||||
|
||||
if (req.body.name && req.body.name !== nodeInfo.name) checkInObject.name = req.body.name
|
||||
if (req.body.ip && req.body.ip !== nodeInfo.ip) checkInObject.ip = req.body.ip
|
||||
if (req.body.port && req.body.port !== nodeInfo.port) checkInObject.port = req.body.port
|
||||
if (req.body.location && req.body.location !== nodeInfo.location) checkInObject.location = req.body.location
|
||||
if (req.body.nearbySystems && JSON.stringify(req.body.nearbySystems) !== JSON.stringify(nodeInfo.nearbySystems)) checkInObject.nearbySystems = req.body.nearbySystems
|
||||
if (req.body.online && req.body.online !== nodeInfo.online) checkInObject.online = req.body.online
|
||||
var isObjectUpdated = false;
|
||||
|
||||
if (req.body.name && req.body.name != nodeInfo.name) {
|
||||
checkInObject._name = req.body.name;
|
||||
isObjectUpdated = true;
|
||||
}
|
||||
|
||||
if (req.body.ip && req.body.ip != nodeInfo.ip) {
|
||||
checkInObject._ip = req.body.ip;
|
||||
isObjectUpdated = true;
|
||||
}
|
||||
|
||||
if (req.body.port && req.body.port != nodeInfo.port) {
|
||||
checkInObject._port = req.body.port;
|
||||
isObjectUpdated = true;
|
||||
}
|
||||
|
||||
if (req.body.location && req.body.location != nodeInfo.location) {
|
||||
checkInObject._location = req.body.location;
|
||||
isObjectUpdated = true;
|
||||
}
|
||||
|
||||
if (req.body.nearbySystems && JSON.stringify(req.body.nearbySystems) !== JSON.stringify(nodeInfo.nearbySystems)) {
|
||||
checkInObject._nearbySystems = req.body.nearbySystems;
|
||||
isObjectUpdated = true;
|
||||
}
|
||||
|
||||
if (req.body.online != nodeInfo.online || req.body.online && (req.body.online === "true") != nodeInfo.online) {
|
||||
checkInObject._online = req.body.online;
|
||||
isObjectUpdated = true;
|
||||
}
|
||||
|
||||
// If no changes are made tell the client
|
||||
if (Object.keys(checkInObject).length === 0) return res.status(200).json("No keys updated");
|
||||
if (!isObjectUpdated) return res.status(200).json("No keys updated");
|
||||
|
||||
log.INFO("Updating the following keys for ID: ", req.body.id, checkInObject);
|
||||
// Adding the ID key to the body so that the client can double-check their ID
|
||||
checkInObject.id = req.body.id;
|
||||
updateNodeInfo(checkInObject, () => {
|
||||
return res.status(202).json({"updatedKeys": checkInObject});
|
||||
})
|
||||
})
|
||||
log.INFO("Updating the following keys for ID: ", req.body.id, checkInObject);
|
||||
|
||||
checkInObject._id = req.body.id;
|
||||
checkInObject = new nodeObject(checkInObject);
|
||||
|
||||
if (!nodeInfo) {
|
||||
log.WARN("No existing node found with this ID, adding node: ", checkInObject);
|
||||
addNewNode(checkInObject, (newNode) => {
|
||||
return res.status(201).json({"updatedKeys": newNode});
|
||||
});
|
||||
}
|
||||
else {
|
||||
updateNodeInfo(checkInObject, () => {
|
||||
return res.status(202).json({"updatedKeys": checkInObject});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -100,7 +132,7 @@ exports.nodeCheckIn = async (req, res) => {
|
||||
*/
|
||||
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)
|
||||
await joinServerWrapper(req.body.presetName, req.body.channelId, req.body.clientId);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -108,6 +140,7 @@ exports.requestNodeJoinServer = async (req, res) => {
|
||||
*/
|
||||
exports.nodeMonitorService = class nodeMonitorService {
|
||||
constructor() {
|
||||
this.log = new DebugBuilder("server", "nodeMonitorService");
|
||||
}
|
||||
|
||||
async start(){
|
||||
@@ -130,21 +163,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);
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
@@ -148,7 +148,7 @@ client.on('ready', () => {
|
||||
runHTTPServer();
|
||||
|
||||
log.DEBUG("Starting Node Monitoring Service");
|
||||
//runNodeMonitorService();
|
||||
runNodeMonitorService();
|
||||
|
||||
log.DEBUG("Starting RSS watcher");
|
||||
runRssService();
|
||||
|
||||
@@ -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();
|
||||
@@ -32,17 +32,38 @@ var runningPostsToRemove = [{
|
||||
}]
|
||||
*/
|
||||
var runningPostsToRemove = {};
|
||||
const sourceFailureLimit = process.env.SOURCE_FAILURE_LIMIT ?? 3;
|
||||
const sourceFailureLimit = process.env.SOURCE_FAILURE_LIMIT ?? 15;
|
||||
|
||||
/**
|
||||
* Wrapper for feeds that cause errors. By default it will wait over a day for the source to come back online before deleting it.
|
||||
*
|
||||
* @param {*} sourceURL
|
||||
* @param {string} sourceURL The URL of the feed source causing issues
|
||||
*/
|
||||
exports.removeSource = function removeSource(sourceURL) {
|
||||
log.INFO("Removing source URL: ", sourceURL);
|
||||
if (!sourceURL in runningPostsToRemove) {runningPostsToRemove[sourceURL] = 1; return;}
|
||||
// Check to see if this is the first time this source has been attempted
|
||||
if (!Object.keys(runningPostsToRemove).includes(sourceURL)) {
|
||||
runningPostsToRemove[sourceURL] = { count: 1, timestamp: Date.now(), ignoredAttempts: 0 };
|
||||
return;
|
||||
}
|
||||
|
||||
const backoffDateTimeDifference = (Date.now() - new Date(runningPostsToRemove[sourceURL].timestamp));
|
||||
const backoffWaitTime = (runningPostsToRemove[sourceURL].count * 30000);
|
||||
|
||||
log.DEBUG("Datetime", runningPostsToRemove[sourceURL], backoffDateTimeDifference, backoffWaitTime);
|
||||
|
||||
// Check to see if the last error occurred within the backoff period or if we should try again
|
||||
if (backoffDateTimeDifference <= backoffWaitTime) {
|
||||
runningPostsToRemove[sourceURL].ignoredAttempts +=1;
|
||||
return;
|
||||
}
|
||||
|
||||
if (runningPostsToRemove[sourceURL] < sourceFailureLimit) {runningPostsToRemove[sourceURL] += 1; return;}
|
||||
// Increase the retry counter
|
||||
if (runningPostsToRemove[sourceURL].count < sourceFailureLimit) {
|
||||
runningPostsToRemove[sourceURL].count += 1;
|
||||
runningPostsToRemove[sourceURL].timestamp = Date.now();
|
||||
return;
|
||||
}
|
||||
|
||||
feedStorage.getRecordBy('link', sourceURL, (err, record) => {
|
||||
if (err) log.ERROR("Error getting record from feedStorage", err);
|
||||
@@ -62,13 +83,14 @@ exports.removeSource = function removeSource(sourceURL) {
|
||||
/**
|
||||
* Unset a source URL from deletion if the source has not already been deleted
|
||||
* @param {*} sourceURL The source URL to be unset from deletion
|
||||
* @returns {*}
|
||||
*/
|
||||
exports.unsetRemoveSource = function unsetRemoveSource(sourceURL) {
|
||||
log.INFO("Unsetting source URL from deletion (if not already deleted): ", sourceURL);
|
||||
if (!sourceURL in runningPostsToRemove) return;
|
||||
if (!Object.keys(runningPostsToRemove).includes(sourceURL)) return;
|
||||
|
||||
if (runningPostsToRemove[sourceURL] > sourceFailureLimit) return delete runningPostsToRemove[sourceURL];
|
||||
delete runningPostsToRemove[sourceURL];
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
@@ -480,11 +480,11 @@ exports.PostStorage = class PostStorage extends Storage {
|
||||
}
|
||||
|
||||
savePost(_postObject, callback){
|
||||
const tempCreationDate = returnMysqlTime();
|
||||
log.DEBUG("Saving Post Object:", _postObject);
|
||||
const tempCreationDate = returnMysqlTime();
|
||||
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);
|
||||
|
||||
|
||||
@@ -1,189 +0,0 @@
|
||||
import re
|
||||
import json
|
||||
import pandas as pd
|
||||
import requests
|
||||
import os
|
||||
import random
|
||||
from fake_useragent import UserAgent
|
||||
from bs4 import BeautifulSoup
|
||||
from urllib.parse import urlparse, unquote, parse_qs
|
||||
from time import sleep
|
||||
ua = UserAgent()
|
||||
|
||||
|
||||
#simply scrape
|
||||
def scrape(url,**kwargs):
|
||||
|
||||
session=requests.Session()
|
||||
session.headers.update({
|
||||
'User-Agent': ua.random,
|
||||
"authority": "www.zillow.com",
|
||||
"accept": "*/*",
|
||||
"accept-language": "en-US,en;q=0.9",
|
||||
"cache-control": "no-cache",
|
||||
"sec-ch-ua-mobile": "?0",
|
||||
"sec-ch-ua-platform": '^\^"Windows^\^"',
|
||||
"sec-fetch-dest": "empty",
|
||||
"sec-fetch-mode": "cors",
|
||||
"sec-fetch-site": "same-origin",
|
||||
})
|
||||
|
||||
response=session.get(url,**kwargs)
|
||||
|
||||
return response
|
||||
|
||||
# Return all sections with key and attributes
|
||||
def slurp(html, tag, attributes):
|
||||
return BeautifulSoup(html, features="html.parser").findAll(tag, attributes)
|
||||
|
||||
# Returns the first number group from a given string
|
||||
def return_numbers(string):
|
||||
return int(re.findall(r'\d+', string)[0])
|
||||
|
||||
|
||||
class Listing:
|
||||
def __init__(self, address, bedrooms, bathrooms, sqft, price, link):
|
||||
self.address = address
|
||||
self.bedrooms = bedrooms
|
||||
self.bathrooms = bathrooms
|
||||
self.sqft = sqft
|
||||
self.price = price
|
||||
self.link = link
|
||||
|
||||
|
||||
class ScrapeZillowListings:
|
||||
def __init__(self, url):
|
||||
self.parsed_original_url = self.init_check_url(urlparse(url))
|
||||
self.html = scrape(url).text
|
||||
self.listings = []
|
||||
|
||||
def init_check_url(self, parsed_url):
|
||||
# Check to see if we are requesting listResults
|
||||
print(parsed_url)
|
||||
print(unquote(parsed_url.query))
|
||||
print(parse_qs(parsed_url.query)['wants'])
|
||||
for want in parse_qs(parsed_url.query)['wants']:
|
||||
print(unquote(unquote(want)))
|
||||
|
||||
return parsed_url
|
||||
|
||||
def run(self):
|
||||
self.listings.extend(self.scrape_listings(self.html))
|
||||
pages = []
|
||||
for page_nav in slurp(self.html, "nav", {"role":"navigation", "aria-label":"Pagination"}):
|
||||
page_nav = f"<html><head><head/><body>{page_nav}<body/><html/>"
|
||||
pages_list = slurp(page_nav, "li", {})
|
||||
for page in pages_list:
|
||||
if re.match("\d{1,2}", page.text) and not page.text == "1":
|
||||
parsed_url = self.setup_url(page.find('a').get('href'))
|
||||
sleep(random.randint(0,15))
|
||||
temp_html = scrape(parsed_url.geturl()).text
|
||||
self.listings.extend(self.scrape_listings(temp_html))
|
||||
|
||||
return self.listings
|
||||
|
||||
def print_listings(self):
|
||||
index = 0
|
||||
for temp_listing in self.listings:
|
||||
print("--------")
|
||||
print(f"Listing #{index}")
|
||||
print(temp_listing.address)
|
||||
print(temp_listing.price)
|
||||
print(temp_listing.bedrooms)
|
||||
print(temp_listing.bathrooms)
|
||||
print(temp_listing.sqft)
|
||||
print(temp_listing.link)
|
||||
print("--------")
|
||||
index += 1
|
||||
|
||||
def scrape_listings(self, html):
|
||||
temp_listings = []
|
||||
for listing in slurp(html, "article", {"data-test":"property-card"}):
|
||||
listing = f"<html><head><head/><body>{listing}<body/><html/>"
|
||||
|
||||
uls = slurp(listing, "li", {})
|
||||
beds = 0
|
||||
baths = 0
|
||||
sqft = 0
|
||||
for ul in uls:
|
||||
ul = ul.get_text()
|
||||
if ("bds" in str(ul)):
|
||||
beds = return_numbers(ul)
|
||||
if ("ba" in str(ul)):
|
||||
baths = return_numbers(ul)
|
||||
if ("sqft" in str(ul)):
|
||||
sqft = return_numbers(ul)
|
||||
|
||||
temp_listings.append(Listing(
|
||||
address=slurp(listing, "address", {"data-test":"property-card-addr"})[0].get_text(),
|
||||
bedrooms=beds,
|
||||
bathrooms=baths,
|
||||
sqft=sqft,
|
||||
price=slurp(listing, "span", {"data-test":"property-card-price"})[0].get_text(),
|
||||
link=slurp(listing, "a", {"data-test":"property-card-link"})[0].get('href'),
|
||||
))
|
||||
|
||||
return temp_listings
|
||||
|
||||
def setup_url(self, url):
|
||||
parsed_url = urlparse(url)
|
||||
print(parsed_url)
|
||||
if not parsed_url.netloc:
|
||||
return urlparse(f"{self.parsed_original_url.scheme}://{self.parsed_original_url.netloc}{parsed_url.path}{self.parsed_original_url.query}{self.parsed_original_url.params}")
|
||||
|
||||
#create dataframe
|
||||
def etl(response):
|
||||
|
||||
#regex to find the data
|
||||
|
||||
for listing in listings:
|
||||
print("--------")
|
||||
print(listing)
|
||||
print("--------")
|
||||
|
||||
print("FORCE STOP")
|
||||
exit()
|
||||
|
||||
#convert text to dict via json
|
||||
dicts=[json.loads('{'+i+'}') for i in num]
|
||||
|
||||
#create dataframe
|
||||
df=pd.DataFrame()
|
||||
for ind,val in enumerate(text):
|
||||
df[val]=dicts[ind].values()
|
||||
df.index=dicts[ind].keys()
|
||||
|
||||
return df
|
||||
|
||||
|
||||
def main():
|
||||
#scrapper = ScrapeZillowListings('https://www.zillow.com/westchester-county-ny/?searchQueryState=%7B%22usersSearchTerm%22%3A%22Yorktown%20Heights%2C%20NY%22%2C%22mapBounds%22%3A%7B%22north%22%3A41.69948153143324%2C%22east%22%3A-72.68804025585938%2C%22south%22%3A40.83865274682678%2C%22west%22%3A-74.29479074414063%7D%2C%22isMapVisible%22%3Atrue%2C%22filterState%22%3A%7B%22price%22%3A%7B%22max%22%3A250000%7D%2C%22ah%22%3A%7B%22value%22%3Atrue%7D%2C%22sort%22%3A%7B%22value%22%3A%22days%22%7D%2C%22land%22%3A%7B%22value%22%3Afalse%7D%2C%22cmsn%22%3A%7B%22value%22%3Afalse%7D%2C%22sche%22%3A%7B%22value%22%3Afalse%7D%2C%22schm%22%3A%7B%22value%22%3Afalse%7D%2C%22schh%22%3A%7B%22value%22%3Afalse%7D%2C%22schp%22%3A%7B%22value%22%3Afalse%7D%2C%22schr%22%3A%7B%22value%22%3Afalse%7D%2C%22schc%22%3A%7B%22value%22%3Afalse%7D%2C%22schu%22%3A%7B%22value%22%3Afalse%7D%7D%2C%22isListVisible%22%3Atrue%2C%22regionSelection%22%3A%5B%7B%22regionId%22%3A3148%2C%22regionType%22%3A4%7D%2C%7B%22regionId%22%3A2694%2C%22regionType%22%3A4%7D%5D%2C%22pagination%22%3A%7B%7D%7D')
|
||||
scrapper = ScrapeZillowListings("https://www.zillow.com/search/GetSearchPageState.htm?searchQueryState=^%^7B^%^22pagination^%^22^%^3A^%^7B^%^7D^%^2C^%^22usersSearchTerm^%^22^%^3A^%^22Yorktown^%^20Heights^%^2C^%^20NY^%^22^%^2C^%^22mapBounds^%^22^%^3A^%^7B^%^22north^%^22^%^3A42.99146217894271^%^2C^%^22east^%^22^%^3A-70.80209903627659^%^2C^%^22south^%^22^%^3A39.549453943310084^%^2C^%^22west^%^22^%^3A-77.00937442690159^%^7D^%^2C^%^22mapZoom^%^22^%^3A8^%^2C^%^22regionSelection^%^22^%^3A^%^5B^%^7B^%^22regionId^%^22^%^3A3148^%^2C^%^22regionType^%^22^%^3A4^%^7D^%^2C^%^7B^%^22regionId^%^22^%^3A2694^%^2C^%^22regionType^%^22^%^3A4^%^7D^%^5D^%^2C^%^22isMapVisible^%^22^%^3Atrue^%^2C^%^22filterState^%^22^%^3A^%^7B^%^22price^%^22^%^3A^%^7B^%^22max^%^22^%^3A250000^%^7D^%^2C^%^22isAllHomes^%^22^%^3A^%^7B^%^22value^%^22^%^3Atrue^%^7D^%^2C^%^22sortSelection^%^22^%^3A^%^7B^%^22value^%^22^%^3A^%^22days^%^22^%^7D^%^2C^%^22isLotLand^%^22^%^3A^%^7B^%^22value^%^22^%^3Afalse^%^7D^%^2C^%^22isMiddleSchool^%^22^%^3A^%^7B^%^22value^%^22^%^3Afalse^%^7D^%^2C^%^22isHighSchool^%^22^%^3A^%^7B^%^22value^%^22^%^3Afalse^%^7D^%^2C^%^22includeUnratedSchools^%^22^%^3A^%^7B^%^22value^%^22^%^3Afalse^%^7D^%^2C^%^22isComingSoon^%^22^%^3A^%^7B^%^22value^%^22^%^3Afalse^%^7D^%^2C^%^22isPublicSchool^%^22^%^3A^%^7B^%^22value^%^22^%^3Afalse^%^7D^%^2C^%^22isPrivateSchool^%^22^%^3A^%^7B^%^22value^%^22^%^3Afalse^%^7D^%^2C^%^22isElementarySchool^%^22^%^3A^%^7B^%^22value^%^22^%^3Afalse^%^7D^%^2C^%^22isCharterSchool^%^22^%^3A^%^7B^%^22value^%^22^%^3Afalse^%^7D^%^7D^%^2C^%^22isListVisible^%^22^%^3Atrue^%^7D&wants=^\{^%^22cat1^%^22:^\[^%^22mapResults^%^22^\]^\}&requestId=3")
|
||||
listings = scrapper.run()
|
||||
scrapper.print_listings()
|
||||
|
||||
#df=etl(response)
|
||||
|
||||
return
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
|
||||
#curl "https://www.zillow.com/search/GetSearchPageState.htm?searchQueryState=^%^7B^%^22pagination^%^22^%^3A^%^7B^%^7D^%^2C^%^22usersSearchTerm^%^22^%^3A^%^22Yorktown^%^20Heights^%^2C^%^20NY^%^22^%^2C^%^22mapBounds^%^22^%^3A^%^7B^%^22north^%^22^%^3A42.99146217894271^%^2C^%^22east^%^22^%^3A-70.80209903627659^%^2C^%^22south^%^22^%^3A39.549453943310084^%^2C^%^22west^%^22^%^3A-77.00937442690159^%^7D^%^2C^%^22mapZoom^%^22^%^3A8^%^2C^%^22regionSelection^%^22^%^3A^%^5B^%^7B^%^22regionId^%^22^%^3A3148^%^2C^%^22regionType^%^22^%^3A4^%^7D^%^2C^%^7B^%^22regionId^%^22^%^3A2694^%^2C^%^22regionType^%^22^%^3A4^%^7D^%^5D^%^2C^%^22isMapVisible^%^22^%^3Atrue^%^2C^%^22filterState^%^22^%^3A^%^7B^%^22price^%^22^%^3A^%^7B^%^22max^%^22^%^3A250000^%^7D^%^2C^%^22isAllHomes^%^22^%^3A^%^7B^%^22value^%^22^%^3Atrue^%^7D^%^2C^%^22sortSelection^%^22^%^3A^%^7B^%^22value^%^22^%^3A^%^22days^%^22^%^7D^%^2C^%^22isLotLand^%^22^%^3A^%^7B^%^22value^%^22^%^3Afalse^%^7D^%^2C^%^22isMiddleSchool^%^22^%^3A^%^7B^%^22value^%^22^%^3Afalse^%^7D^%^2C^%^22isHighSchool^%^22^%^3A^%^7B^%^22value^%^22^%^3Afalse^%^7D^%^2C^%^22includeUnratedSchools^%^22^%^3A^%^7B^%^22value^%^22^%^3Afalse^%^7D^%^2C^%^22isComingSoon^%^22^%^3A^%^7B^%^22value^%^22^%^3Afalse^%^7D^%^2C^%^22isPublicSchool^%^22^%^3A^%^7B^%^22value^%^22^%^3Afalse^%^7D^%^2C^%^22isPrivateSchool^%^22^%^3A^%^7B^%^22value^%^22^%^3Afalse^%^7D^%^2C^%^22isElementarySchool^%^22^%^3A^%^7B^%^22value^%^22^%^3Afalse^%^7D^%^2C^%^22isCharterSchool^%^22^%^3A^%^7B^%^22value^%^22^%^3Afalse^%^7D^%^7D^%^2C^%^22isListVisible^%^22^%^3Atrue^%^7D&wants=^\{^%^22cat1^%^22:^\[^%^22mapResults^%^22^\]^\}&requestId=3",
|
||||
# "authority: www.zillow.com",
|
||||
# "accept: */*",
|
||||
# "accept-language: en-US,en;q=0.9",
|
||||
# "cache-control: no-cache",
|
||||
# "cookie: JSESSIONID=97FD1EB701E102B7353E8EA4528843CE; zguid=24^|^%^24825bd6e9-4f90-46df-a475-4a9910b5847c; zgsession=1^|a6a5b7ca-c651-45a2-93c8-c5b66fea68d3; AWSALB=oQ3DGTMPgyQOTPA6zLmQ0liqJ1oax2QoQ5rUSCsORkWP52C7k6G8H1gZnlxOtgU/zzO503UHUnQ7tUeivhOnupv7aYI6+E5LxUZl4TeE0JyhvT3pZ6LYeC9iFbTw; AWSALBCORS=oQ3DGTMPgyQOTPA6zLmQ0liqJ1oax2QoQ5rUSCsORkWP52C7k6G8H1gZnlxOtgU/zzO503UHUnQ7tUeivhOnupv7aYI6+E5LxUZl4TeE0JyhvT3pZ6LYeC9iFbTw; search=6^|1689549806090^%^7Crect^%^3D42.92311815473404^%^252C-70.80209903627659^%^252C39.62142250427077^%^252C-77.00937442690159^%^26rid^%^3D2694^%^26disp^%^3Dmap^%^26mdm^%^3Dauto^%^26p^%^3D1^%^26sort^%^3Ddays^%^26z^%^3D1^%^26listPriceActive^%^3D1^%^26type^%^3Dhouse^%^252Ccondo^%^252Capartment_duplex^%^252Cmobile^%^252Ctownhouse^%^26lt^%^3Dfsba^%^252Cfsbo^%^252Cfore^%^252Cnew^%^252Cauction^%^26price^%^3D0-250000^%^26fs^%^3D1^%^26fr^%^3D0^%^26mmm^%^3D0^%^26rs^%^3D0^%^26ah^%^3D0^%^26singlestory^%^3D0^%^26housing-connector^%^3D0^%^26abo^%^3D0^%^26garage^%^3D0^%^26pool^%^3D0^%^26ac^%^3D0^%^26waterfront^%^3D0^%^26finished^%^3D0^%^26unfinished^%^3D0^%^26cityview^%^3D0^%^26mountainview^%^3D0^%^26parkview^%^3D0^%^26waterview^%^3D0^%^26hoadata^%^3D1^%^26zillow-owned^%^3D0^%^263dhome^%^3D0^%^26featuredMultiFamilyBuilding^%^3D0^%^26commuteMode^%^3Ddriving^%^26commuteTimeOfDay^%^3Dnow^%^09^%^092694^%^09^%^09^%^09^%^09^%^09^%^09",
|
||||
# "pragma: no-cache",
|
||||
# "sec-ch-ua: ^\^"Not.A/Brand^\^";v=^\^"8^\^", ^\^"Chromium^\^";v=^\^"114^\^", ^\^"Google Chrome^\^";v=^\^"114^\^"",
|
||||
# "sec-ch-ua-mobile: ?0",
|
||||
# "sec-ch-ua-platform: ^\^"Windows^\^"",
|
||||
# "sec-fetch-dest: empty",
|
||||
# "sec-fetch-mode: cors",
|
||||
# "sec-fetch-site: same-origin",
|
||||
# "user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36",
|
||||
# "x-kl-ajax-request: Ajax_Request",
|
||||
# --compressed
|
||||
@@ -1,4 +0,0 @@
|
||||
pandas
|
||||
requests
|
||||
fake-useragent
|
||||
beautifulsoup4
|
||||
150
Server/package-lock.json
generated
150
Server/package-lock.json
generated
@@ -26,7 +26,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",
|
||||
@@ -489,14 +489,6 @@
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
|
||||
},
|
||||
"node_modules/bignumber.js": {
|
||||
"version": "9.0.0",
|
||||
"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.0.tgz",
|
||||
"integrity": "sha512-t/OYhhJ2SD+YGBQcjY8GzzDHEk9f3nerxjtfa6tlMXfe7frs/WozhvCNoGvpM0P3bNf3Gq5ZRMlGr5f3r4/N8A==",
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/bluebird": {
|
||||
"version": "3.7.2",
|
||||
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
|
||||
@@ -736,11 +728,6 @@
|
||||
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
|
||||
"integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ=="
|
||||
},
|
||||
"node_modules/core-util-is": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
|
||||
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="
|
||||
},
|
||||
"node_modules/css-select": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz",
|
||||
@@ -805,6 +792,14 @@
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/denque": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz",
|
||||
"integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==",
|
||||
"engines": {
|
||||
"node": ">=0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/depd": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
|
||||
@@ -1284,6 +1279,14 @@
|
||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
|
||||
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
|
||||
},
|
||||
"node_modules/generate-function": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz",
|
||||
"integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==",
|
||||
"dependencies": {
|
||||
"is-property": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/get-intrinsic": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz",
|
||||
@@ -1510,10 +1513,10 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/isarray": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
|
||||
"integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="
|
||||
"node_modules/is-property": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz",
|
||||
"integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g=="
|
||||
},
|
||||
"node_modules/jake": {
|
||||
"version": "10.8.7",
|
||||
@@ -1662,6 +1665,11 @@
|
||||
"resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz",
|
||||
"integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw=="
|
||||
},
|
||||
"node_modules/long": {
|
||||
"version": "5.2.3",
|
||||
"resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz",
|
||||
"integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q=="
|
||||
},
|
||||
"node_modules/lru-cache": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
|
||||
@@ -1846,24 +1854,69 @@
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||
},
|
||||
"node_modules/mysql": {
|
||||
"version": "2.18.1",
|
||||
"resolved": "https://registry.npmjs.org/mysql/-/mysql-2.18.1.tgz",
|
||||
"integrity": "sha512-Bca+gk2YWmqp2Uf6k5NFEurwY/0td0cpebAucFpY/3jhrwrVGuxU2uQFCHjU19SJfje0yQvi+rVWdq78hR5lig==",
|
||||
"node_modules/mysql2": {
|
||||
"version": "3.3.5",
|
||||
"resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.3.5.tgz",
|
||||
"integrity": "sha512-ZTQGAzxGeaX1PyeSiZFCgQ34uiXguaEpn3aTFN9Enm9JDnbwWo+4/CJnDdQZ3n0NaMeysi8vwtW/jNUb9VqVDw==",
|
||||
"dependencies": {
|
||||
"bignumber.js": "9.0.0",
|
||||
"readable-stream": "2.3.7",
|
||||
"safe-buffer": "5.1.2",
|
||||
"sqlstring": "2.3.1"
|
||||
"denque": "^2.1.0",
|
||||
"generate-function": "^2.3.1",
|
||||
"iconv-lite": "^0.6.3",
|
||||
"long": "^5.2.1",
|
||||
"lru-cache": "^8.0.0",
|
||||
"named-placeholders": "^1.1.3",
|
||||
"seq-queue": "^0.0.5",
|
||||
"sqlstring": "^2.3.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/mysql2/node_modules/iconv-lite": {
|
||||
"version": "0.6.3",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
||||
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
|
||||
"dependencies": {
|
||||
"safer-buffer": ">= 2.1.2 < 3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/mysql2/node_modules/lru-cache": {
|
||||
"version": "8.0.5",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-8.0.5.tgz",
|
||||
"integrity": "sha512-MhWWlVnuab1RG5/zMRRcVGXZLCXrZTgfwMikgzCegsPnG62yDQo5JnqKkrK4jO5iKqDAZGItAqN5CtKBCBWRUA==",
|
||||
"engines": {
|
||||
"node": ">=16.14"
|
||||
}
|
||||
},
|
||||
"node_modules/mysql2/node_modules/sqlstring": {
|
||||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.3.tgz",
|
||||
"integrity": "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/mysql/node_modules/safe-buffer": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
|
||||
"node_modules/named-placeholders": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.3.tgz",
|
||||
"integrity": "sha512-eLoBxg6wE/rZkJPhU/xRX1WTpkFEwDJEN96oxFrTsqBdbT5ec295Q+CoHrL9IT0DipqKhmGcaZmwOt8OON5x1w==",
|
||||
"dependencies": {
|
||||
"lru-cache": "^7.14.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/named-placeholders/node_modules/lru-cache": {
|
||||
"version": "7.18.3",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz",
|
||||
"integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/negotiator": {
|
||||
"version": "0.6.3",
|
||||
@@ -2209,11 +2262,6 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/process-nextick-args": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
|
||||
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
|
||||
},
|
||||
"node_modules/proxy-addr": {
|
||||
"version": "2.0.7",
|
||||
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
|
||||
@@ -2319,25 +2367,6 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/readable-stream": {
|
||||
"version": "2.3.7",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
|
||||
"integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
|
||||
"dependencies": {
|
||||
"core-util-is": "~1.0.0",
|
||||
"inherits": "~2.0.3",
|
||||
"isarray": "~1.0.0",
|
||||
"process-nextick-args": "~2.0.0",
|
||||
"safe-buffer": "~5.1.1",
|
||||
"string_decoder": "~1.1.1",
|
||||
"util-deprecate": "~1.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/readable-stream/node_modules/safe-buffer": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
|
||||
},
|
||||
"node_modules/readable-web-to-node-stream": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.2.tgz",
|
||||
@@ -2475,6 +2504,11 @@
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
|
||||
},
|
||||
"node_modules/seq-queue": {
|
||||
"version": "0.0.5",
|
||||
"resolved": "https://registry.npmjs.org/seq-queue/-/seq-queue-0.0.5.tgz",
|
||||
"integrity": "sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q=="
|
||||
},
|
||||
"node_modules/serve-static": {
|
||||
"version": "1.15.0",
|
||||
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz",
|
||||
@@ -2546,14 +2580,6 @@
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/sqlstring": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.1.tgz",
|
||||
"integrity": "sha512-ooAzh/7dxIG5+uDik1z/Rd1vli0+38izZhGzSa34FwR7IbelPWCCKSNIl8jlL/F7ERvy8CB2jNeM1E9i9mXMAQ==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/statuses": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
})()
|
||||
}
|
||||
}
|
||||
@@ -40,7 +40,7 @@ exports.sendHttpRequest = function sendHttpRequest(requestOptions, data, callbac
|
||||
res.on('data', (data) => {
|
||||
const responseObject = {
|
||||
"statusCode": res.statusCode,
|
||||
"body": (isJsonString(data.toString)) ? JSON.parse(data) : data.toString()
|
||||
"body": (isJsonString(data.toString())) ? JSON.parse(data.toString()) : data.toString()
|
||||
};
|
||||
log.DEBUG("Response Object: ", responseObject);
|
||||
callback(responseObject);
|
||||
|
||||
@@ -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");
|
||||
@@ -24,6 +24,8 @@ const nodeConnectionsTable = `${process.env.NODE_DB_NAME}.node_connections`;
|
||||
* @returns {nodeObject} The converted node object to be used downstream
|
||||
*/
|
||||
function returnNodeObjectFromRow(row) {
|
||||
if (!isNaN(row.online)) row.online = Boolean(row.online);
|
||||
else if (row.online == "true" || row.online == "false") row.online = (row.online == "true");
|
||||
return new nodeObject({
|
||||
_id: row.id,
|
||||
_name: row.name,
|
||||
@@ -31,7 +33,7 @@ function returnNodeObjectFromRow(row) {
|
||||
_port: row.port,
|
||||
_location: row.location,
|
||||
_nearbySystems: BufferToJson(row.nearbySystems),
|
||||
_online: (row.online === 1) ? true : false,
|
||||
_online: row.online,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -92,7 +94,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 +112,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 +124,8 @@ 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]);
|
||||
if (!sqlResponse.length > 0) return (callback) ? callback(false) : false;
|
||||
return (callback) ? callback(await returnNodeObjectFromRow(sqlResponse[0])) : await returnNodeObjectFromRow(sqlResponse[0]);
|
||||
}
|
||||
exports.getNodeInfoFromId = getNodeInfoFromId
|
||||
|
||||
@@ -136,10 +139,15 @@ 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 ?? {})
|
||||
|
||||
var online = nodeObject.online;
|
||||
if (typeof online === "boolean" || typeof online === "number") {
|
||||
if (online) online = 1;
|
||||
else online = 0;
|
||||
}
|
||||
|
||||
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 +157,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 +216,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 +363,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
|
||||
})
|
||||
}
|
||||
|
||||
@@ -109,18 +109,17 @@ class nodeObject {
|
||||
* @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 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._online True/False if the node is online or offline
|
||||
* @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 = undefined, _name = undefined, _ip = undefined, _port = undefined, _location = undefined, _nearbySystems = undefined, _online = undefined }) {
|
||||
this.id = _id;
|
||||
this.name = _name;
|
||||
this.ip = _ip;
|
||||
this.port = _port;
|
||||
this.location = _location;
|
||||
this.location = _location;
|
||||
this.nearbySystems = _nearbySystems;
|
||||
if (this.nearbySystems) this.presets = Object.keys(_nearbySystems);
|
||||
this.online = _online;
|
||||
}
|
||||
}
|
||||
@@ -137,7 +136,7 @@ class clientObject {
|
||||
* @param {*} param0._name The name of the bot associated with the IDs
|
||||
* @param {*} param0._client_id The client ID of the bot needed to connect to Discord
|
||||
*/
|
||||
constructor({_discord_id = null, _name = null, _client_id = null,}) {
|
||||
constructor({_discord_id = undefined, _name = undefined, _client_id = undefined,}) {
|
||||
this.discordId = _discord_id;
|
||||
this.name = _name;
|
||||
this.clientId = _client_id;
|
||||
@@ -156,7 +155,7 @@ class connectionObject {
|
||||
* @param {*} param0._node The node associated with the connection
|
||||
* @param {*} param0._client_object The client object associated with the connection
|
||||
*/
|
||||
constructor({_connection_id = null, _node = null, _client_object}) {
|
||||
constructor({_connection_id = undefined, _node = undefined, _client_object}) {
|
||||
this.connectionId = _connection_id;
|
||||
this.node = _node;
|
||||
this.clientObject = _client_object;
|
||||
|
||||
@@ -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
|
||||
@@ -116,4 +117,20 @@ 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 })),
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user