diff --git a/client/client.js b/client/client.js new file mode 100644 index 0000000..ed5d26c --- /dev/null +++ b/client/client.js @@ -0,0 +1,45 @@ +import { generateUniqueID } from './modules/baseUtils.mjs'; +import { updateId } from './modules/updateConfig.mjs'; +import { ClientNodeConfig } from './modules/clientObjectDefinitions.mjs'; +import { initSocketConnection, initSocketListeners, sendNodeUpdate } from './modules/socketClient.mjs'; + +import dotenv from 'dotenv'; +dotenv.config() + +var localNodeConfig = new ClientNodeConfig({}) + +async function boot() { + if (localNodeConfig.node.id === undefined || localNodeConfig.node.id === '' || localNodeConfig.node.id === 0) { + // Run the first time boot sequence + await firstTimeBoot(); + } + // Initialize the socket connection with the server + return initSocketConnection(); +} + +/** + * Run the first time the client boots on a pc + * @returns {any} + */ +async function firstTimeBoot() { + // Generate a new ID for the node + localNodeConfig.node.id = generateUniqueID(); + console.log(`Generated a new unique ID for this node: '${localNodeConfig.node.id}'`); + + // Update the config with the new ID + updateId(localNodeConfig.node.id); + console.log("Updated the config with the new node ID"); + // TODO - Create the config file with the ID given and replace the update above + + // TODO - Implement web server so users can update radio systems easily + // TODO - Implement logic to check if the presets are set + return +} + + +// Boot the client application +boot().then((openSocket) => { + initSocketListeners(openSocket); + //console.log(openSocket, "Open socket"); + setTimeout(() => {sendNodeUpdate(openSocket);}, 2500); +}) \ No newline at end of file diff --git a/client/modules/baseUtils.mjs b/client/modules/baseUtils.mjs new file mode 100644 index 0000000..62c0931 --- /dev/null +++ b/client/modules/baseUtils.mjs @@ -0,0 +1,49 @@ +import { networkInterfaces } from 'os'; +import { createHash, randomBytes } from 'crypto'; + +/** + * Check to see if the input is a valid JSON string + * + * @param {*} str The string to check for valud JSON + * @returns {true|false} + */ +export function isJsonString (str) { + try { + JSON.parse(str); + } catch (e) { + return false; + } + return true; +} + + +/** + * Generate a unique ID for a given device, this will also be unique on the same device at a different time. + * @returns {string} A generated unique ID + */ +export function generateUniqueID () { + const netInterfaces = networkInterfaces(); + let macAddress = ''; + + // Find the first non-internal MAC address + for (const key in netInterfaces) { + const iface = netInterfaces[key][0]; + if (!iface.internal) { + macAddress = iface.mac; + break; + } + } + + // If no non-internal MAC address is found, fallback to a random value + if (!macAddress) { + macAddress = randomBytes(6).toString('hex').toUpperCase(); + } + + // Use MAC address and current timestamp to create a unique ID + const timestamp = Date.now(); + const uniqueID = createHash('sha256') + .update(macAddress + timestamp) + .digest('hex'); + + return uniqueID; +} \ No newline at end of file diff --git a/client/modules/clientObjectDefinitions.mjs b/client/modules/clientObjectDefinitions.mjs new file mode 100644 index 0000000..52a97cf --- /dev/null +++ b/client/modules/clientObjectDefinitions.mjs @@ -0,0 +1,40 @@ +import dotenv from 'dotenv'; +dotenv.config() + +/** + * + */ +export class ClientNodeObject { + /** + * + * @param {string} param0._id The ID of the node (Assigned by the client on first boot) + * @param {string} param0._name The name of the node (Assigned by the user) + * @param {string} param0._location The physical location of the node (Assigned by the user) + * @param {object} param0._nearbySystems An object array of nearby systems (Assigned by the user) + */ + constructor({ _id = undefined, _name = undefined, _location = undefined, _nearbySystems = undefined}) { + this.id = _id; + this.name = _name; + this.location = _location; + this.nearbySystems = _nearbySystems; + } +} + + +/** The configuration object for the node */ +export class ClientNodeConfig { + constructor({ + _id = process.env.CLIENT_ID, + _name = process.env.CLIENT_NAME, + _location = process.env.CLIENT_LOCATION, + _nearbySystems = undefined, // TODO - Get the presets when loading the config instead of having to do it after the fact + _serverIp = process.env.SERVER_IP, + _serverPort = process.env.SERVER_PORT, + }) { + this.node = new ClientNodeObject({ + _id:_id, _name:_name, _location:_location, _nearbySystems:_nearbySystems + }); + this.serverIp = _serverIp; + this.serverPort = _serverPort; + } +} \ No newline at end of file diff --git a/client/modules/socketClient.mjs b/client/modules/socketClient.mjs new file mode 100644 index 0000000..06e5b19 --- /dev/null +++ b/client/modules/socketClient.mjs @@ -0,0 +1,37 @@ +import { io } from "socket.io-client"; + +export function initSocketConnection() { + const serverEndpoint = `http://${localNodeConfig.serverIp}:${localNodeConfig.serverPort}` || 'http://localhost:3000'; // Adjust the server endpoint + + const socket = io.connect(serverEndpoint); + + return socket; +} + +export function initSocketListeners(socket){ + socket.on('connect', () => { + console.log('Connected to the server'); + logIntoServer(socket); + }); + + socket.on('node-join', (joinData) => { + console.log("Join requested: ", joinData) + }); + + socket.on('node-leave', () => { + console.log("Leave requested"); + }); + + socket.on('disconnect', () => { + console.log('Disconnected from the server'); + }); + +} + +export function logIntoServer(socket, nodeData) { + socket.emit("node-login", nodeData); +} + +export function sendNodeUpdate(socket, nodeData) { + socket.emit('node-update', nodeData); +} \ No newline at end of file diff --git a/client/modules/updateConfig.mjs b/client/modules/updateConfig.mjs new file mode 100644 index 0000000..a9a4640 --- /dev/null +++ b/client/modules/updateConfig.mjs @@ -0,0 +1,107 @@ +// Modules +import replace from 'replace-in-file'; + +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"); + // 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}`; + } +} + +/** + * Wrapper to update the client's saved ID + * @param updatedId The updated ID assigned to the node + */ +export function updateId (updatedId) { + updateConfig('CLIENT_ID', updatedId); + process.env.CLIENT_ID = updatedId; + console.log("Updated ID to: ", updatedId); +} + +/** + * Wrapper to update any or all keys in the client config + * + * @param {Object} runningConfig Running config object + * @param {Object} newConfigObject Object with what keys you wish to update (node object format, will be converted) + * @param {number} newConfigObject.id The ID given to the node to update + * @param {string} newConfigObject.name The name of the node + * @param {string} newConfigObject.ip The IP the server can contact the node on + * @param {number} newConfigObject.port The port the server can contact the node on + * @param {string} newConfigObject.location The physical location of the node + * @returns + */ +export function updateClientConfig (runningConfig, newConfigObject) { + var updatedKeys = [] + const configKeys = Object.keys(newConfigObject); + + if (configKeys.includes("id")) { + if (runningConfig.id != newConfigObject.id) { + this.updateId(newConfigObject.id); + updatedKeys.push({ 'CLIENT_ID': newConfigObject.id }); + } + } + if (configKeys.includes("name")) { + if (runningConfig.name != newConfigObject.name) { + this.updateConfig('CLIENT_NAME', newConfigObject.name); + updatedKeys.push({ 'CLIENT_NAME': newConfigObject.name }); + process.env.CLIENT_NAME = newConfigObject.name; + console.log("Updated name to: ", newConfigObject.name); + } + } + if (configKeys.includes("ip")) { + if (runningConfig.ip != newConfigObject.ip) { + this.updateConfig('CLIENT_IP', newConfigObject.ip); + updatedKeys.push({ 'CLIENT_IP': newConfigObject.ip }); + process.env.CLIENT_IP = newConfigObject.ip; + console.log("Updated ip to: ", newConfigObject.ip); + } + } + if (configKeys.includes("port")) { + if (runningConfig.port != newConfigObject.port) { + this.updateConfig('CLIENT_PORT', newConfigObject.port); + updatedKeys.push({ 'CLIENT_PORT': newConfigObject.port }); + process.env.CLIENT_PORT = newConfigObject.port; + console.log("Updated port to: ", newConfigObject.port); + } + } + if (configKeys.includes("location")) { + if (runningConfig.location != newConfigObject.location) { + this.updateConfig('CLIENT_LOCATION', newConfigObject.location); + updatedKeys.push({ 'CLIENT_LOCATION': newConfigObject.location }); + process.env.CLIENT_LOCATION = newConfigObject.location; + console.log("Updated location to: ", newConfigObject.location); + } + } + + return updatedKeys; +} + +/** + * + * @param {string} key The config file key to update with the value + * @param {string} value The value to update the key with + */ +export function updateConfig (key, value) { + const options = new Options(key, value); + + updateConfigFile(options, (updatedFiles) => { + // Do Something + }) +} + +/** + * Wrapper to write changes to the file + * @param options An instance of the Objects class specified to the key being updated + * @param callback Callback when the files have been modified + */ +function updateConfigFile(options, callback) { + replace(options, (error, changedFiles) => { + if (error) return console.error('Error occurred:', error); + console.log('Updated config file: ', changedFiles); + callback(changedFiles); + }); +} \ No newline at end of file diff --git a/client/package-lock.json b/client/package-lock.json new file mode 100644 index 0000000..a29bf5a --- /dev/null +++ b/client/package-lock.json @@ -0,0 +1,791 @@ +{ + "name": "drb-client", + "version": "3.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "drb-client", + "version": "3.0.0", + "license": "ISC", + "dependencies": { + "@discordjs/voice": "^0.16.1", + "discord.js": "^14.14.1", + "dotenv": "^16.3.1", + "libsodium-wrappers": "^0.7.13", + "prism-media": "^1.3.5", + "replace-in-file": "^7.1.0", + "socket.io-client": "^4.7.2" + }, + "devDependencies": { + "typescript": "^5.3.3" + } + }, + "node_modules/@discordjs/builders": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-1.7.0.tgz", + "integrity": "sha512-GDtbKMkg433cOZur8Dv6c25EHxduNIBsxeHrsRoIM8+AwmEZ8r0tEpckx/sHwTLwQPOF3e2JWloZh9ofCaMfAw==", + "dependencies": { + "@discordjs/formatters": "^0.3.3", + "@discordjs/util": "^1.0.2", + "@sapphire/shapeshift": "^3.9.3", + "discord-api-types": "0.37.61", + "fast-deep-equal": "^3.1.3", + "ts-mixer": "^6.0.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.11.0" + } + }, + "node_modules/@discordjs/collection": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-1.5.3.tgz", + "integrity": "sha512-SVb428OMd3WO1paV3rm6tSjM4wC+Kecaa1EUGX7vc6/fddvw/6lg90z4QtCqm21zvVe92vMMDt9+DkIvjXImQQ==", + "engines": { + "node": ">=16.11.0" + } + }, + "node_modules/@discordjs/formatters": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@discordjs/formatters/-/formatters-0.3.3.tgz", + "integrity": "sha512-wTcI1Q5cps1eSGhl6+6AzzZkBBlVrBdc9IUhJbijRgVjCNIIIZPgqnUj3ntFODsHrdbGU8BEG9XmDQmgEEYn3w==", + "dependencies": { + "discord-api-types": "0.37.61" + }, + "engines": { + "node": ">=16.11.0" + } + }, + "node_modules/@discordjs/rest": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@discordjs/rest/-/rest-2.2.0.tgz", + "integrity": "sha512-nXm9wT8oqrYFRMEqTXQx9DUTeEtXUDMmnUKIhZn6O2EeDY9VCdwj23XCPq7fkqMPKdF7ldAfeVKyxxFdbZl59A==", + "dependencies": { + "@discordjs/collection": "^2.0.0", + "@discordjs/util": "^1.0.2", + "@sapphire/async-queue": "^1.5.0", + "@sapphire/snowflake": "^3.5.1", + "@vladfrangu/async_event_emitter": "^2.2.2", + "discord-api-types": "0.37.61", + "magic-bytes.js": "^1.5.0", + "tslib": "^2.6.2", + "undici": "5.27.2" + }, + "engines": { + "node": ">=16.11.0" + } + }, + "node_modules/@discordjs/rest/node_modules/@discordjs/collection": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-2.0.0.tgz", + "integrity": "sha512-YTWIXLrf5FsrLMycpMM9Q6vnZoR/lN2AWX23/Cuo8uOOtS8eHB2dyQaaGnaF8aZPYnttf2bkLMcXn/j6JUOi3w==", + "engines": { + "node": ">=18" + } + }, + "node_modules/@discordjs/util": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@discordjs/util/-/util-1.0.2.tgz", + "integrity": "sha512-IRNbimrmfb75GMNEjyznqM1tkI7HrZOf14njX7tCAAUetyZM1Pr8hX/EK2lxBCOgWDRmigbp24fD1hdMfQK5lw==", + "engines": { + "node": ">=16.11.0" + } + }, + "node_modules/@discordjs/voice": { + "version": "0.16.1", + "resolved": "https://registry.npmjs.org/@discordjs/voice/-/voice-0.16.1.tgz", + "integrity": "sha512-uiWiW0Ta6K473yf8zs13RfKuPqm/xU4m4dAidMkIdwqgy1CztbbZBtPLfDkVSKzpW7s6m072C+uQcs4LwF3FhA==", + "dependencies": { + "@types/ws": "^8.5.9", + "discord-api-types": "0.37.61", + "prism-media": "^1.3.5", + "tslib": "^2.6.2", + "ws": "^8.14.2" + }, + "engines": { + "node": ">=16.11.0" + } + }, + "node_modules/@discordjs/voice/node_modules/ws": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz", + "integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/@discordjs/ws": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@discordjs/ws/-/ws-1.0.2.tgz", + "integrity": "sha512-+XI82Rm2hKnFwAySXEep4A7Kfoowt6weO6381jgW+wVdTpMS/56qCvoXyFRY0slcv7c/U8My2PwIB2/wEaAh7Q==", + "dependencies": { + "@discordjs/collection": "^2.0.0", + "@discordjs/rest": "^2.1.0", + "@discordjs/util": "^1.0.2", + "@sapphire/async-queue": "^1.5.0", + "@types/ws": "^8.5.9", + "@vladfrangu/async_event_emitter": "^2.2.2", + "discord-api-types": "0.37.61", + "tslib": "^2.6.2", + "ws": "^8.14.2" + }, + "engines": { + "node": ">=16.11.0" + } + }, + "node_modules/@discordjs/ws/node_modules/@discordjs/collection": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-2.0.0.tgz", + "integrity": "sha512-YTWIXLrf5FsrLMycpMM9Q6vnZoR/lN2AWX23/Cuo8uOOtS8eHB2dyQaaGnaF8aZPYnttf2bkLMcXn/j6JUOi3w==", + "engines": { + "node": ">=18" + } + }, + "node_modules/@discordjs/ws/node_modules/ws": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz", + "integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/@fastify/busboy": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.0.tgz", + "integrity": "sha512-+KpH+QxZU7O4675t3mnkQKcZZg56u+K/Ct2K+N2AZYNVK8kyeo/bI18tI8aPm3tvNNRyTWfj6s5tnGNlcbQRsA==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@sapphire/async-queue": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@sapphire/async-queue/-/async-queue-1.5.1.tgz", + "integrity": "sha512-1RdpsmDQR/aWfp8oJzPtn4dNQrbpqSL5PIA0uAB/XwerPXUf994Ug1au1e7uGcD7ei8/F63UDjr5GWps1g/HxQ==", + "engines": { + "node": ">=v14.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/@sapphire/shapeshift": { + "version": "3.9.5", + "resolved": "https://registry.npmjs.org/@sapphire/shapeshift/-/shapeshift-3.9.5.tgz", + "integrity": "sha512-AGdHe+51gF7D3W8hBfuSFLBocURDCXVQczScTHXDS3RpNjNgrktIx/amlz5y8nHhm8SAdFt/X8EF8ZSfjJ0tnA==", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "lodash": "^4.17.21" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@sapphire/snowflake": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/@sapphire/snowflake/-/snowflake-3.5.1.tgz", + "integrity": "sha512-BxcYGzgEsdlG0dKAyOm0ehLGm2CafIrfQTZGWgkfKYbj+pNNsorZ7EotuZukc2MT70E0UbppVbtpBrqpzVzjNA==", + "engines": { + "node": ">=v14.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/@socket.io/component-emitter": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", + "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==" + }, + "node_modules/@types/node": { + "version": "20.10.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.7.tgz", + "integrity": "sha512-fRbIKb8C/Y2lXxB5eVMj4IU7xpdox0Lh8bUPEdtLysaylsml1hOOx1+STloRs/B9nf7C6kPRmmg/V7aQW7usNg==", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/ws": { + "version": "8.5.10", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz", + "integrity": "sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@vladfrangu/async_event_emitter": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/@vladfrangu/async_event_emitter/-/async_event_emitter-2.2.4.tgz", + "integrity": "sha512-ButUPz9E9cXMLgvAW8aLAKKJJsPu1dY1/l/E8xzLFuysowXygs6GBcyunK9rnGC4zTsnIc2mQo71rGw9U+Ykug==", + "engines": { + "node": ">=v14.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/discord-api-types": { + "version": "0.37.61", + "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.61.tgz", + "integrity": "sha512-o/dXNFfhBpYHpQFdT6FWzeO7pKc838QeeZ9d91CfVAtpr5XLK4B/zYxQbYgPdoMiTDvJfzcsLW5naXgmHGDNXw==" + }, + "node_modules/discord.js": { + "version": "14.14.1", + "resolved": "https://registry.npmjs.org/discord.js/-/discord.js-14.14.1.tgz", + "integrity": "sha512-/hUVzkIerxKHyRKopJy5xejp4MYKDPTszAnpYxzVVv4qJYf+Tkt+jnT2N29PIPschicaEEpXwF2ARrTYHYwQ5w==", + "dependencies": { + "@discordjs/builders": "^1.7.0", + "@discordjs/collection": "1.5.3", + "@discordjs/formatters": "^0.3.3", + "@discordjs/rest": "^2.1.0", + "@discordjs/util": "^1.0.2", + "@discordjs/ws": "^1.0.2", + "@sapphire/snowflake": "3.5.1", + "@types/ws": "8.5.9", + "discord-api-types": "0.37.61", + "fast-deep-equal": "3.1.3", + "lodash.snakecase": "4.1.1", + "tslib": "2.6.2", + "undici": "5.27.2", + "ws": "8.14.2" + }, + "engines": { + "node": ">=16.11.0" + } + }, + "node_modules/discord.js/node_modules/@types/ws": { + "version": "8.5.9", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.9.tgz", + "integrity": "sha512-jbdrY0a8lxfdTp/+r7Z4CkycbOFN8WX+IOchLJr3juT/xzbJ8URyTVSJ/hvNdadTgM1mnedb47n+Y31GsFnQlg==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/discord.js/node_modules/ws": { + "version": "8.14.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.14.2.tgz", + "integrity": "sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/dotenv": { + "version": "16.3.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz", + "integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/motdotla/dotenv?sponsor=1" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/engine.io-client": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.5.3.tgz", + "integrity": "sha512-9Z0qLB0NIisTRt1DZ/8U2k12RJn8yls/nXMZLn+/N8hANT3TcYjKFKcwbw5zFQiN4NTde3TSY9zb79e1ij6j9Q==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.11.0", + "xmlhttprequest-ssl": "~2.0.0" + } + }, + "node_modules/engine.io-parser": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.1.tgz", + "integrity": "sha512-9JktcM3u18nU9N2Lz3bWeBgxVgOKpw7yhRaoxQA3FUDZzzw+9WlA6p4G4u0RixNkg14fH7EfEc/RhpurtiROTQ==", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "engines": { + "node": ">=6" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/libsodium": { + "version": "0.7.13", + "resolved": "https://registry.npmjs.org/libsodium/-/libsodium-0.7.13.tgz", + "integrity": "sha512-mK8ju0fnrKXXfleL53vtp9xiPq5hKM0zbDQtcxQIsSmxNgSxqCj6R7Hl9PkrNe2j29T4yoDaF7DJLK9/i5iWUw==" + }, + "node_modules/libsodium-wrappers": { + "version": "0.7.13", + "resolved": "https://registry.npmjs.org/libsodium-wrappers/-/libsodium-wrappers-0.7.13.tgz", + "integrity": "sha512-kasvDsEi/r1fMzKouIDv7B8I6vNmknXwGiYodErGuESoFTohGSKZplFtVxZqHaoQ217AynyIFgnOVRitpHs0Qw==", + "dependencies": { + "libsodium": "^0.7.13" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "node_modules/lodash.snakecase": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz", + "integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==" + }, + "node_modules/magic-bytes.js": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/magic-bytes.js/-/magic-bytes.js-1.7.0.tgz", + "integrity": "sha512-YzVU2+/hrjwx8xcgAw+ffNq3jkactpj+f1iSL4LonrFKhvnwDzHSqtFdk/MMRP53y9ScouJ7cKEnqYsJwsHoYA==" + }, + "node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/prism-media": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/prism-media/-/prism-media-1.3.5.tgz", + "integrity": "sha512-IQdl0Q01m4LrkN1EGIE9lphov5Hy7WWlH6ulf5QdGePLlPas9p2mhgddTEHrlaXYjjFToM1/rWuwF37VF4taaA==", + "peerDependencies": { + "@discordjs/opus": ">=0.8.0 <1.0.0", + "ffmpeg-static": "^5.0.2 || ^4.2.7 || ^3.0.0 || ^2.4.0", + "node-opus": "^0.3.3", + "opusscript": "^0.0.8" + }, + "peerDependenciesMeta": { + "@discordjs/opus": { + "optional": true + }, + "ffmpeg-static": { + "optional": true + }, + "node-opus": { + "optional": true + }, + "opusscript": { + "optional": true + } + } + }, + "node_modules/replace-in-file": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/replace-in-file/-/replace-in-file-7.1.0.tgz", + "integrity": "sha512-1uZmJ78WtqNYCSuPC9IWbweXkGxPOtk2rKuar8diTw7naVIQZiE3Tm8ACx2PCMXDtVH6N+XxwaRY2qZ2xHPqXw==", + "dependencies": { + "chalk": "^4.1.2", + "glob": "^8.1.0", + "yargs": "^17.7.2" + }, + "bin": { + "replace-in-file": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/socket.io-client": { + "version": "4.7.2", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.7.2.tgz", + "integrity": "sha512-vtA0uD4ibrYD793SOIAwlo8cj6haOeMHrGvwPxJsxH7CeIksqJ+3Zc06RvWTIFgiSqx4A3sOnTXpfAEE2Zyz6w==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.2", + "engine.io-client": "~6.5.2", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-parser": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", + "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ts-mixer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/ts-mixer/-/ts-mixer-6.0.3.tgz", + "integrity": "sha512-k43M7uCG1AkTyxgnmI5MPwKoUvS/bRvLvUb7+Pgpdlmok8AoqmUaZxUUw8zKM5B1lqZrt41GjYgnvAi0fppqgQ==" + }, + "node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/typescript": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", + "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici": { + "version": "5.27.2", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.27.2.tgz", + "integrity": "sha512-iS857PdOEy/y3wlM3yRp+6SNQQ6xU0mmZcwRSriqk+et/cwWAtwmIGf6WkoDN2EK/AMdCO/dfXzIwi+rFMrjjQ==", + "dependencies": { + "@fastify/busboy": "^2.0.0" + }, + "engines": { + "node": ">=14.0" + } + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "node_modules/ws": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", + "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xmlhttprequest-ssl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz", + "integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "engines": { + "node": ">=12" + } + } + } +} diff --git a/client/package.json b/client/package.json new file mode 100644 index 0000000..7a673a4 --- /dev/null +++ b/client/package.json @@ -0,0 +1,22 @@ +{ + "name": "drb-client", + "version": "3.0.0", + "description": "", + "main": "client.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "type": "module", + "dependencies": { + "@discordjs/voice": "^0.16.1", + "discord.js": "^14.14.1", + "dotenv": "^16.3.1", + "libsodium-wrappers": "^0.7.13", + "prism-media": "^1.3.5", + "replace-in-file": "^7.1.0", + "socket.io-client": "^4.7.2" + }, +} diff --git a/server/discordBot/commands/ping.mjs b/server/discordBot/commands/ping.mjs new file mode 100644 index 0000000..4a18209 --- /dev/null +++ b/server/discordBot/commands/ping.mjs @@ -0,0 +1,24 @@ +import { SlashCommandBuilder } from 'discord.js'; + +// Exporting data property +export const data = new SlashCommandBuilder() + .setName('ping') + .setDescription('Replies with your input!'); + +// Exporting other properties +export const example = "/ping"; +export const deferInitialReply = false; + +// Exporting execute function +export async function execute(nodeIo, interaction) { + try { + const sockets = await nodeIo.allSockets(); + console.log("All open sockets: ",sockets); + //await interaction.reply(`**Online Sockets: '${sockets}'**`); + await interaction.reply('**Pong.**'); + //await interaction.channel.send('**Pong.**'); + } catch (err) { + console.error(err); + // await interaction.reply(err.toString()); + } +} \ No newline at end of file diff --git a/server/discordBot/events/interactionCreate.mjs b/server/discordBot/events/interactionCreate.mjs new file mode 100644 index 0000000..64bb668 --- /dev/null +++ b/server/discordBot/events/interactionCreate.mjs @@ -0,0 +1,28 @@ +import { Events } from 'discord.js'; + +export const name = Events.InteractionCreate; + +export async function execute(nodeIo, interaction) { + const command = interaction.client.commands.get(interaction.commandName); + console.log("Interaction created for command: ", command); + + // Execute autocomplete if the user is checking autocomplete + /* + if (interaction.isAutocomplete()) { + console.log("Running autocomplete for command: ", command.data.name); + return await command.autocomplete(interaction); + } + */ + + // Check if the interaction is a command + if (!interaction.isChatInputCommand()) return; + + if (!command) { + console.error(`No command matching ${interaction.commandName} was found.`); + return; + } + + console.log(`${interaction.member.user} is running '${interaction.commandName}'`); + + command.execute(nodeIo, interaction); +} \ No newline at end of file diff --git a/server/discordBot/modules/deployCommands.mjs b/server/discordBot/modules/deployCommands.mjs new file mode 100644 index 0000000..51bcd54 --- /dev/null +++ b/server/discordBot/modules/deployCommands.mjs @@ -0,0 +1,80 @@ +import { REST, Routes } from 'discord.js'; + +import dotenv from 'dotenv'; +dotenv.config() + +//const clientId = process.env.clientId; +//const guildId = process.env.guildId; + +const fs = require('node:fs'); +const path = require('node:path'); + +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')); + +export function deploy (clientId, guildIDs) { + console.log("Deploying commands for: ", guildIDs); + if (!Array.isArray(guildIDs)) guildIDs = [guildIDs]; + // Grab the SlashCommandBuilder#toJSON() output of each command's data for deployment + for (const file of commandFiles) { + const command = require(`${path.resolve(commandsPath, file)}`); + console.log('Deploying Command: ', command); + commands.push(command.data.toJSON()); + } + + // Construct and prepare an instance of the REST module + const rest = new REST({ version: '10' }).setToken(process.env.DISCORD_TOKEN); + + // and deploy your commands! + for (const guildId of guildIDs) { + (async () => { + try { + console.log(`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 }, + ); + + console.log(`Successfully reloaded ${data.length} application (/) commands for guild ID: ${guildId}.`); + } catch (error) { + // And of course, make sure you catch and log any errors! + console.log("ERROR Deploying commands: ", error, "Body from error: ", commands); + } + })() + } +}; + +/** + * 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 + */ +export function removeAll (clientId, guildId) { + if (!Array.isArray(guildId)) guildIDs = [guildId]; + console.log("Removing commands for: ", clientId, guildIDs); + + commands = []; + + const rest = new REST({ version: '10' }).setToken(process.env.DISCORD_TOKEN); + for (const guildId of guildIDs) { + (async () => { + try { + console.log(`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 }, + ); + + console.log(`Successfully reloaded ${data.length} application (/) commands for guild ID: ${guildId}.`); + } catch (error) { + // And of course, make sure you catch and log any errors! + console.log("ERROR Deploying commands: ", error, "Body from error: ", commands); + } + })() + } +} \ No newline at end of file diff --git a/server/discordBot/modules/discordBotUtils.mjs b/server/discordBot/modules/discordBotUtils.mjs new file mode 100644 index 0000000..9e1bb72 --- /dev/null +++ b/server/discordBot/modules/discordBotUtils.mjs @@ -0,0 +1,69 @@ +import { join, dirname } from 'path'; +import { readdirSync } from 'fs'; +import { fileURLToPath } from 'url'; +import { Collection } from 'discord.js'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); + +/** + * Add the enabled commands to the bot to be used by users in discord + * (commands that end in '.mjs' will be enabled, to disable just remove the extension or replace with '.mjs.disabled') + * @param {any} serverClient + * @param {any} nodeIo + * @param {any} _commandsPath="./commands" + * @returns {any} + */ +export function addEnabledCommands(serverClient, nodeIo, _commandsPath = "../commands") { + // Setup commands for the Discord bot + serverClient.commands = new Collection(); + const commandsPath = join(__dirname, _commandsPath); + const commandFiles = readdirSync(commandsPath).filter(file => file.endsWith('.mjs')); + + for (const file of commandFiles) { + const filePath = join(commandsPath, file); + console.log(`Adding enabled command: ${filePath}`); + import(`file://${filePath}`).then(command => { + if (command.data instanceof Promise) { + command.data.then(async (builder) => { + command.data = builder; + console.log("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 + serverClient.commands.set(command.data.name, command); + }); + } else { + console.log("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 + serverClient.commands.set(command.data.name, command); + } + }) + } +} + +/** + * Add the enabled event listeners to the bot + * (events that end in '.mjs' will be enabled, to disable just remove the extension or replace with '.mjs.disabled') + * @param {any} serverClient + * @param {any} nodeIo + * @param {any} _eventsPath="./events" + * @returns {any} + */ +export function addEnabledEventListeners(serverClient, nodeIo, _eventsPath = "../events") { + const eventsPath = join(__dirname, _eventsPath); + const eventFiles = readdirSync(eventsPath).filter(file => file.endsWith('.mjs')); + + for (const file of eventFiles) { + const filePath = join(eventsPath, file); + console.log(`Adding enabled event listener: ${filePath}`); + import(`file://${filePath}`).then(event => { + console.log("Adding event: ", event); + if (event.once) { + serverClient.once(event.name, (...args) => event.execute(nodeIo, ...args)); + } else { + serverClient.on(event.name, (...args) => event.execute(nodeIo, ...args)); + } + }) + } +} \ No newline at end of file diff --git a/server/modules/discordBot.mjs b/server/modules/discordBot.mjs new file mode 100644 index 0000000..08e76a4 --- /dev/null +++ b/server/modules/discordBot.mjs @@ -0,0 +1,9 @@ +import { Client, GatewayIntentBits } from 'discord.js'; +import dotenv from 'dotenv'; +dotenv.config() + +export const serverClient = new Client({ intents: [GatewayIntentBits.Guilds] }); + +serverClient.on('ready', () => { + console.log(`Logged in as ${serverClient.user.tag}!`); +}); \ No newline at end of file diff --git a/server/modules/socketServer.mjs b/server/modules/socketServer.mjs new file mode 100644 index 0000000..68b1dfc --- /dev/null +++ b/server/modules/socketServer.mjs @@ -0,0 +1,59 @@ +import express from 'express'; +import { createServer } from 'node:http'; +import { Server } from 'socket.io'; +import morgan from 'morgan'; + +export const app = express(); +export const server = createServer(app); +export const nodeIo = new Server(server); + +app.use(morgan('tiny')); + +app.get('/', (req, res) => { + res.send('