├── .dockerignore ├── .env.example ├── .gitignore ├── Dockerfile ├── README.md ├── backend ├── deleteCommands.js └── deployCommands.js ├── commands ├── allContainers.js ├── ping.js ├── restart.js ├── server.js ├── startContainer.js └── stopContainer.js ├── docker-compose.yml ├── index.js ├── package-lock.json └── package.json /.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .env 3 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | DISCORD_TOKEN= 2 | DISCORD_CLIENT_ID= 3 | DISCORD_GUILD_ID= -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .env -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:19-slim 2 | 3 | WORKDIR /app 4 | 5 | COPY package.json /app 6 | RUN npm install 7 | 8 | COPY . . 9 | 10 | ENV DISCORD_TOKEN "" \ 11 | DISCORD_CLIENT_ID "" \ 12 | DISCORD_GUILD_ID "" 13 | 14 | CMD [ "node", "index.js" ] -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Server-Bot 3 | 4 | [View on Docker Hub](https://hub.docker.com/r/allenrkeen/server-bot) 5 | ### Discord bot to remotely monitor and control a docker based server. Using the docker socket. 6 | 7 | Setup is pretty straightforward. 8 | 1. Create a new application in the *[discord developer portal](https://discord.com/developers/applications)* 9 | 2. Go to the bot section and click *Add Bot* 10 | 3. Reset Token and keep the token somewhere secure (This will be referred to as "DISCORD_TOKEN" in .env and docker environment variables) 11 | 4. Get the "Application ID" from the General Information tab of your application (This will be referred to as "DISCORD_CLIENT_ID" in .env and docker environment variables) 12 | 5. *Optional:* If you have developer mode enabled in Discord, get your server's ID by right-clicking on the server name and clicking "Copy ID" (This will be referred to as "DISCORD_GUILD_ID" in .env and docker environment variables) 13 | - If you skip this, it will still work, but commands will be published globally instead of to your server and can take up to an hour to be available in your server. 14 | - Using the Server ID will be more secure, making the commands available only in the specified server. 15 | 6. Run the application in your preffered method. 16 | - Run the docker container with the provided [docker-compose.yml](docker-compose.yml) or the docker run command below. 17 | 18 | ```bash 19 | docker run -v /var/run/docker.sock:/var/run/docker.sock --name server-bot \ 20 | -e DISCORD_TOKEN=your_token_here \ 21 | -e DISCORD_CLIENT_ID=your_client_id_here \ 22 | -e DISCORD_GUILD_ID=your_guild_id_here \ 23 | allenrkeen/server-bot:latest 24 | ``` 25 | 26 | - Clone the repo, cd into the server-bot directory and run "npm install" to install dependencies, then "npm run start" to start the server 27 | 7. The program will build an invite link with the correct permissions and put it in the logs. Click the link and confirm the server to add the bot to. 28 | 29 | 30 | Current commands: 31 | - /allcontainers 32 | - provides container name and status for all containers 33 | - /restartcontainer 34 | - provides an autocomplete list of running containers to select from, or just type in container name then restarts the container 35 | - /stopcontainer 36 | - provides an autocomplete list of running containers to select from, or just type in container name then stops the container 37 | - /startcontainer 38 | - provides an autocomplete list of stopped containers to select from, or just type in container name then starts the container 39 | - /ping 40 | - Replies with "Pong!" when the bot is listening 41 | - /server 42 | - Replies with Server Name and member count, good for testing. 43 | -------------------------------------------------------------------------------- /backend/deleteCommands.js: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is used to delete all commands from the Discord API. 3 | * Only use this if you want to delete all commands and understand the consequences. 4 | */ 5 | 6 | require('dotenv').config(); 7 | const token = process.env.DISCORD_TOKEN; 8 | const clientID = process.env.DISCORD_CLIENT_ID; 9 | const guildID = process.env.DISCORD_GUILD_ID; 10 | const { REST, Routes } = require('discord.js'); 11 | const fs = require('node:fs'); 12 | 13 | const rest = new REST({ version: '10' }).setToken(token); 14 | 15 | rest.put(Routes.applicationCommands(clientID), { body: [] }) 16 | .then(() => console.log('Successfully deleted application (/) commands.')) 17 | .catch(console.error); 18 | 19 | rest.put(Routes.applicationGuildCommands(clientID, guildID), { body: [] }) 20 | .then(() => console.log('Successfully deleted guild (/) commands.')) 21 | .catch(console.error); 22 | 23 | -------------------------------------------------------------------------------- /backend/deployCommands.js: -------------------------------------------------------------------------------- 1 | /* 2 | This script pushes all commands in the commands folder to be usable in discord. 3 | */ 4 | 5 | require('dotenv').config(); 6 | const token = process.env.DISCORD_TOKEN; 7 | const clientID = process.env.DISCORD_CLIENT_ID; 8 | const guildID = process.env.DISCORD_GUILD_ID; 9 | const { REST, Routes } = require('discord.js'); 10 | const fs = require('node:fs'); 11 | 12 | const commands = []; 13 | 14 | // Get all commands from the commands folder 15 | 16 | const commandFiles = fs.readdirSync('./commands').filter(file => file.endsWith('.js')); 17 | console.log(commandFiles); 18 | 19 | for (const file of commandFiles) { 20 | const command = require(`../commands/${file}`); 21 | commands.push(command.data.toJSON()); 22 | } 23 | 24 | const rest = new REST({ version: '10' }).setToken(token); 25 | 26 | // console.log(commands); 27 | 28 | (async () => { 29 | try { 30 | const rest = new REST({ version: '10' }).setToken(token); 31 | 32 | console.log('Started refreshing application (/) commands.'); 33 | 34 | //publish to guild if guildID is set, otherwise publish to global 35 | if (guildID) { 36 | const data = await rest.put( 37 | Routes.applicationGuildCommands(clientID, guildID), 38 | { body: commands }, 39 | ); 40 | console.log('Successfully reloaded '+ data.length +' commands.'); 41 | } else { 42 | const data = await rest.put( 43 | Routes.applicationCommands(clientID), 44 | { body: commands }, 45 | ); 46 | console.log('Successfully reloaded '+ data.length +' commands.'); 47 | } 48 | 49 | } catch (error) { 50 | console.error(error); 51 | } 52 | })(); 53 | 54 | -------------------------------------------------------------------------------- /commands/allContainers.js: -------------------------------------------------------------------------------- 1 | /* A command that lists all containers with their status */ 2 | 3 | const { SlashCommandBuilder, EmbedBuilder } = require("discord.js"); 4 | const Docker = require('node-docker-api').Docker; 5 | 6 | module.exports = { 7 | data: new SlashCommandBuilder() 8 | .setName("allcontainers") 9 | .setDescription("Lists all containers"), 10 | async execute(interaction) { 11 | outArray = []; 12 | interaction.reply('Listing all containers...'); 13 | 14 | //create docker client 15 | const docker = new Docker({ socketPath: '/var/run/docker.sock' }); 16 | 17 | // get all containers 18 | const containers = await docker.container.list({ all: true}); 19 | 20 | // create array of containers with name and status 21 | outArray = containers.map(c => { 22 | return { 23 | name: c.data.Names[0].slice(1), 24 | status: c.data.State 25 | }; 26 | }); 27 | 28 | embedCount = Math.ceil(outArray.length / 25); 29 | for (let i = 0; i < embedCount; i++) { 30 | const embed = new EmbedBuilder() 31 | .setTitle('Containers') 32 | .addFields(outArray.slice(i * 25, (i + 1) * 25).map(e => { 33 | return { name: e.name, value: e.status }; 34 | })) 35 | .setColor(0x00AE86); 36 | interaction.channel.send({ embeds: [embed] }); 37 | } 38 | }, 39 | }; -------------------------------------------------------------------------------- /commands/ping.js: -------------------------------------------------------------------------------- 1 | /* 2 | A ping command that replies with "Pong!" when bot is running. 3 | */ 4 | 5 | const { SlashCommandBuilder } = require("discord.js"); 6 | 7 | module.exports = { 8 | data: new SlashCommandBuilder() 9 | .setName("ping") 10 | .setDescription("Replies with Pong!"), 11 | async execute(interaction) { 12 | await interaction.reply("Pong!"); 13 | }, 14 | }; -------------------------------------------------------------------------------- /commands/restart.js: -------------------------------------------------------------------------------- 1 | const { SlashCommandBuilder, EmbedBuilder } = require("discord.js"); 2 | const Docker = require('node-docker-api').Docker; 3 | 4 | module.exports = { 5 | data: new SlashCommandBuilder() 6 | .setName("restartcontainer") 7 | .setDescription("Restarts a Docker container") 8 | .addStringOption(option => 9 | option.setName('container') 10 | .setDescription('The container to restart') 11 | .setRequired(true) 12 | .setAutocomplete(true)), 13 | async autocomplete(interaction) { 14 | try { 15 | // Create docker client 16 | const docker = new Docker({ socketPath: '/var/run/docker.sock' }); 17 | 18 | // Get list of running containers 19 | const containers = await docker.container.list({ all: true, filters: { status: ['running'] } }); 20 | const runningContainers = containers.map(c => c.data.Names[0].slice(1)); 21 | 22 | // Filter list of containers by focused value 23 | const focusedValue = interaction.options.getFocused(true); 24 | const filteredContainers = runningContainers.filter(container => container.startsWith(focusedValue.value)); 25 | 26 | //slice if more than 25 27 | let sliced; 28 | if (filteredContainers.length > 25) { 29 | sliced = filteredContainers.slice(0, 25); 30 | } else { 31 | sliced = filteredContainers; 32 | } 33 | 34 | // Respond with filtered list of containers 35 | await interaction.respond(sliced.map(container => ({ name: container, value: container }))); 36 | 37 | } catch (error) { 38 | // Handle error 39 | console.error(error); 40 | await interaction.reply('An error occurred while getting the list of running containers.'); 41 | } 42 | }, 43 | async execute(interaction) { 44 | try { 45 | // create docker client 46 | const docker = new Docker({ socketPath: '/var/run/docker.sock' }); 47 | 48 | // Get container name from options 49 | const container = interaction.options.getString('container'); 50 | 51 | // Restart container 52 | await interaction.reply(`Restarting container "${container}"...`); 53 | const containers = await docker.container.list({ all: true, filters: { name: [container] } }); 54 | if (containers.length === 0) { 55 | await interaction.followUp(`Container "${container}" does not exist.`); 56 | throw new Error(`Container "${container}" does not exist.`); 57 | } 58 | await containers[0].restart(); 59 | 60 | 61 | // Confirm that container was restarted 62 | await interaction.followUp(`Container "${container}" was successfully restarted.`); 63 | } catch (error) { 64 | // Handle error 65 | console.error(error); 66 | await interaction.followUp(`An error occurred while trying to restart the container "${container}".`); 67 | } 68 | } 69 | }; 70 | -------------------------------------------------------------------------------- /commands/server.js: -------------------------------------------------------------------------------- 1 | const { SlashCommandBuilder } = require('discord.js'); 2 | 3 | module.exports = { 4 | data: new SlashCommandBuilder() 5 | .setName("server") 6 | .setDescription("Replies with server name and member count."), 7 | async execute(interaction) { 8 | await interaction.reply(`Server name: ${interaction.guild.name}\nTotal members: ${interaction.guild.memberCount}`); 9 | }, 10 | }; 11 | -------------------------------------------------------------------------------- /commands/startContainer.js: -------------------------------------------------------------------------------- 1 | const { SlashCommandBuilder, EmbedBuilder } = require("discord.js"); 2 | const Docker = require('node-docker-api').Docker; 3 | 4 | module.exports = { 5 | data: new SlashCommandBuilder() 6 | .setName("startcontainer") 7 | .setDescription("Starts a Docker container") 8 | .addStringOption(option => 9 | option.setName('container') 10 | .setDescription('The container to start') 11 | .setRequired(true) 12 | .setAutocomplete(true)), 13 | async autocomplete(interaction) { 14 | try { 15 | // Create docker client 16 | const docker = new Docker({ socketPath: '/var/run/docker.sock' }); 17 | 18 | // Get list of running containers 19 | const containers = await docker.container.list({ all: true, filters: { status: ['exited'] } }); 20 | const runningContainers = containers.map(c => c.data.Names[0].slice(1)); 21 | 22 | // Filter list of containers by focused value 23 | const focusedValue = interaction.options.getFocused(true); 24 | const filteredContainers = runningContainers.filter(container => container.startsWith(focusedValue.value)); 25 | 26 | //slice if more than 25 27 | let sliced; 28 | if (filteredContainers.length > 25) { 29 | sliced = filteredContainers.slice(0, 25); 30 | } else { 31 | sliced = filteredContainers; 32 | } 33 | 34 | // Respond with filtered list of containers 35 | await interaction.respond(sliced.map(container => ({ name: container, value: container }))); 36 | 37 | } catch (error) { 38 | // Handle error 39 | console.error(error); 40 | await interaction.reply('An error occurred while getting the list of running containers.'); 41 | } 42 | }, 43 | async execute(interaction) { 44 | try { 45 | // create docker client 46 | const docker = new Docker({ socketPath: '/var/run/docker.sock' }); 47 | 48 | // Get container name from options 49 | const container = interaction.options.getString('container'); 50 | 51 | // Restart container 52 | await interaction.reply(`Starting container "${container}"...`); 53 | const containers = await docker.container.list({ all: true, filters: { name: [container] } }); 54 | if (containers.length === 0) { 55 | await interaction.followUp(`Container "${container}" does not exist.`); 56 | throw new Error(`Container "${container}" does not exist.`); 57 | } 58 | await containers[0].start(); 59 | 60 | // Confirm that container was restarted 61 | await interaction.followUp(`Container "${container}" was successfully started.`); 62 | } catch (error) { 63 | // Handle error 64 | console.error(error); 65 | await interaction.followUp(`An error occurred while trying to start the container "${container}".`); 66 | } 67 | }, 68 | }; -------------------------------------------------------------------------------- /commands/stopContainer.js: -------------------------------------------------------------------------------- 1 | const { SlashCommandBuilder, EmbedBuilder } = require("discord.js"); 2 | const Docker = require('node-docker-api').Docker; 3 | 4 | module.exports = { 5 | data: new SlashCommandBuilder() 6 | .setName("stopcontainer") 7 | .setDescription("Stops a Docker container") 8 | .addStringOption(option => 9 | option.setName('container') 10 | .setDescription('The container to stop') 11 | .setRequired(true) 12 | .setAutocomplete(true)), 13 | async autocomplete(interaction) { 14 | try { 15 | // Create docker client 16 | const docker = new Docker({ socketPath: '/var/run/docker.sock' }); 17 | 18 | // Get list of running containers 19 | const containers = await docker.container.list({ all: true, filters: { status: ['running'] } }); 20 | const runningContainers = containers.map(c => c.data.Names[0].slice(1)); 21 | 22 | // Filter list of containers by focused value 23 | const focusedValue = interaction.options.getFocused(true); 24 | const filteredContainers = runningContainers.filter(container => container.startsWith(focusedValue.value)); 25 | 26 | //slice if more than 25 27 | let sliced; 28 | if (filteredContainers.length > 25) { 29 | sliced = filteredContainers.slice(0, 25); 30 | } else { 31 | sliced = filteredContainers; 32 | } 33 | 34 | // Respond with filtered list of containers 35 | await interaction.respond(sliced.map(container => ({ name: container, value: container }))); 36 | 37 | } catch (error) { 38 | // Handle error 39 | console.error(error); 40 | await interaction.reply('An error occurred while getting the list of running containers.'); 41 | } 42 | }, 43 | async execute(interaction) { 44 | try { 45 | // create docker client 46 | const docker = new Docker({ socketPath: '/var/run/docker.sock' }); 47 | 48 | // Get container name from options 49 | const container = interaction.options.getString('container'); 50 | 51 | // Restart container 52 | await interaction.reply(`Stopping container "${container}"...`); 53 | const containers = await docker.container.list({ all: true, filters: { name: [container] } }); 54 | if (containers.length === 0) { 55 | await interaction.followUp(`Container "${container}" does not exist.`); 56 | throw new Error(`Container "${container}" does not exist.`); 57 | } 58 | await containers[0].stop(); 59 | 60 | // Confirm that container was restarted 61 | await interaction.followUp(`Container "${container}" was successfully stopped.`); 62 | } catch (error) { 63 | // Handle error 64 | console.error(error); 65 | await interaction.followUp(`An error occurred while trying to stop the container "${container}".`); 66 | } 67 | } 68 | }; 69 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | services: 4 | server-bot: 5 | container_name: server-bot 6 | image: allenrkeen/server-bot:latest 7 | volumes: 8 | - /var/run/docker.sock:/var/run/docker.sock #required 9 | environment: 10 | - DISCORD_TOKEN=your_token_here #required 11 | - DISCORD_CLIENT_ID=your_client_id_here #required 12 | - DISCORD_GUILD_ID=your_guild_id_here #optional -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const fs = require('node:fs'); 3 | const path = require('node:path'); 4 | const token = process.env.DISCORD_TOKEN; 5 | const clientID = process.env.DISCORD_CLIENT_ID; 6 | 7 | // Require the necessary discord.js classes 8 | const { Client, Collection, Events, GatewayIntentBits } = require('discord.js'); 9 | 10 | // Create a new client instance 11 | const client = new Client({ intents: [GatewayIntentBits.Guilds] }); 12 | 13 | //run backend/deployCommands.js 14 | const { exec } = require('child_process'); 15 | exec('node backend/deployCommands.js', (err, stdout, stderr) => { 16 | if (err) { 17 | //some err occurred 18 | console.error(err); 19 | } else { 20 | // print complete output 21 | console.log(stdout); 22 | } 23 | }); 24 | 25 | 26 | 27 | // When the client is ready, run this code 28 | client.once(Events.ClientReady, c => { 29 | console.log(`Ready! Logged in as ${c.user.tag}`); 30 | }); 31 | 32 | // Log in to Discord with your client's token 33 | client.login(token); 34 | 35 | // Create a new collection for commands 36 | client.commands = new Collection(); 37 | 38 | const commandsPath = path.join(__dirname, 'commands'); 39 | const commandFiles = fs.readdirSync(commandsPath).filter(file => file.endsWith('.js')); 40 | 41 | for (const file of commandFiles) { 42 | const filePath = path.join(commandsPath, file); 43 | const command = require(filePath); 44 | // Set a new item in the Collection with the key as the name of the command and the value as the exported module 45 | if ('data' in command && 'execute' in command) { 46 | client.commands.set(command.data.name, command); 47 | } else { 48 | console.log(`Command ${file} is missing 'data' or 'execute'`); 49 | } 50 | } 51 | //build and display invite link 52 | const inviteLink = 'https://discord.com/oauth2/authorize?client_id='+clientID+'&permissions=2147534912&scope=bot%20applications.commands'; 53 | 54 | console.log(`Invite link: ${inviteLink}`); 55 | 56 | // execute on slash command 57 | client.on(Events.InteractionCreate, async interaction => { 58 | if (interaction.isChatInputCommand()) { 59 | const command = client.commands.get(interaction.commandName); 60 | 61 | if (!command) { 62 | console.error('No command matching ${interaction.commandName} was found.'); 63 | return; 64 | } 65 | 66 | try { 67 | await command.execute(interaction); 68 | } catch (error) { 69 | console.error(error); 70 | // await interaction.reply({ content: 'There was an error while executing this command!', ephemeral: true }); 71 | } 72 | } else if (interaction.isAutocomplete()) { 73 | 74 | const command = client.commands.get(interaction.commandName); 75 | 76 | if (!command) { 77 | console.error('No command matching ${interaction.commandName} was found.'); 78 | return; 79 | } 80 | 81 | try { 82 | await command.autocomplete(interaction); 83 | } catch (error) { 84 | console.error(error); 85 | // await interaction.({ content: 'There was an error while executing this command!', ephemeral: true }); 86 | } 87 | } 88 | }); 89 | 90 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "server-bot", 3 | "version": "1.0.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "server-bot", 9 | "version": "1.0.0", 10 | "license": "MIT", 11 | "dependencies": { 12 | "discord.js": "^14.7.1", 13 | "dockerode": "^3.3.4", 14 | "dotenv": "^16.0.3", 15 | "node-docker-api": "^1.1.22" 16 | } 17 | }, 18 | "node_modules/@balena/dockerignore": { 19 | "version": "1.0.2", 20 | "resolved": "https://registry.npmjs.org/@balena/dockerignore/-/dockerignore-1.0.2.tgz", 21 | "integrity": "sha512-wMue2Sy4GAVTk6Ic4tJVcnfdau+gx2EnG7S+uAEe+TWJFqE4YoWN4/H8MSLj4eYJKxGg26lZwboEniNiNwZQ6Q==" 22 | }, 23 | "node_modules/@discordjs/builders": { 24 | "version": "1.4.0", 25 | "resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-1.4.0.tgz", 26 | "integrity": "sha512-nEeTCheTTDw5kO93faM1j8ZJPonAX86qpq/QVoznnSa8WWcCgJpjlu6GylfINTDW6o7zZY0my2SYdxx2mfNwGA==", 27 | "dependencies": { 28 | "@discordjs/util": "^0.1.0", 29 | "@sapphire/shapeshift": "^3.7.1", 30 | "discord-api-types": "^0.37.20", 31 | "fast-deep-equal": "^3.1.3", 32 | "ts-mixer": "^6.0.2", 33 | "tslib": "^2.4.1" 34 | }, 35 | "engines": { 36 | "node": ">=16.9.0" 37 | } 38 | }, 39 | "node_modules/@discordjs/collection": { 40 | "version": "1.3.0", 41 | "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-1.3.0.tgz", 42 | "integrity": "sha512-ylt2NyZ77bJbRij4h9u/wVy7qYw/aDqQLWnadjvDqW/WoWCxrsX6M3CIw9GVP5xcGCDxsrKj5e0r5evuFYwrKg==", 43 | "engines": { 44 | "node": ">=16.9.0" 45 | } 46 | }, 47 | "node_modules/@discordjs/rest": { 48 | "version": "1.5.0", 49 | "resolved": "https://registry.npmjs.org/@discordjs/rest/-/rest-1.5.0.tgz", 50 | "integrity": "sha512-lXgNFqHnbmzp5u81W0+frdXN6Etf4EUi8FAPcWpSykKd8hmlWh1xy6BmE0bsJypU1pxohaA8lQCgp70NUI3uzA==", 51 | "dependencies": { 52 | "@discordjs/collection": "^1.3.0", 53 | "@discordjs/util": "^0.1.0", 54 | "@sapphire/async-queue": "^1.5.0", 55 | "@sapphire/snowflake": "^3.2.2", 56 | "discord-api-types": "^0.37.23", 57 | "file-type": "^18.0.0", 58 | "tslib": "^2.4.1", 59 | "undici": "^5.13.0" 60 | }, 61 | "engines": { 62 | "node": ">=16.9.0" 63 | } 64 | }, 65 | "node_modules/@discordjs/util": { 66 | "version": "0.1.0", 67 | "resolved": "https://registry.npmjs.org/@discordjs/util/-/util-0.1.0.tgz", 68 | "integrity": "sha512-e7d+PaTLVQav6rOc2tojh2y6FE8S7REkqLldq1XF4soCx74XB/DIjbVbVLtBemf0nLW77ntz0v+o5DytKwFNLQ==", 69 | "engines": { 70 | "node": ">=16.9.0" 71 | } 72 | }, 73 | "node_modules/@sapphire/async-queue": { 74 | "version": "1.5.0", 75 | "resolved": "https://registry.npmjs.org/@sapphire/async-queue/-/async-queue-1.5.0.tgz", 76 | "integrity": "sha512-JkLdIsP8fPAdh9ZZjrbHWR/+mZj0wvKS5ICibcLrRI1j84UmLMshx5n9QmL8b95d4onJ2xxiyugTgSAX7AalmA==", 77 | "engines": { 78 | "node": ">=v14.0.0", 79 | "npm": ">=7.0.0" 80 | } 81 | }, 82 | "node_modules/@sapphire/shapeshift": { 83 | "version": "3.8.1", 84 | "resolved": "https://registry.npmjs.org/@sapphire/shapeshift/-/shapeshift-3.8.1.tgz", 85 | "integrity": "sha512-xG1oXXBhCjPKbxrRTlox9ddaZTvVpOhYLmKmApD/vIWOV1xEYXnpoFs68zHIZBGbqztq6FrUPNPerIrO1Hqeaw==", 86 | "dependencies": { 87 | "fast-deep-equal": "^3.1.3", 88 | "lodash": "^4.17.21" 89 | }, 90 | "engines": { 91 | "node": ">=v14.0.0", 92 | "npm": ">=7.0.0" 93 | } 94 | }, 95 | "node_modules/@sapphire/snowflake": { 96 | "version": "3.4.0", 97 | "resolved": "https://registry.npmjs.org/@sapphire/snowflake/-/snowflake-3.4.0.tgz", 98 | "integrity": "sha512-zZxymtVO6zeXVMPds+6d7gv/OfnCc25M1Z+7ZLB0oPmeMTPeRWVPQSS16oDJy5ZsyCOLj7M6mbZml5gWXcVRNw==", 99 | "engines": { 100 | "node": ">=v14.0.0", 101 | "npm": ">=7.0.0" 102 | } 103 | }, 104 | "node_modules/@tokenizer/token": { 105 | "version": "0.3.0", 106 | "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", 107 | "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==" 108 | }, 109 | "node_modules/@types/node": { 110 | "version": "18.11.18", 111 | "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.18.tgz", 112 | "integrity": "sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==" 113 | }, 114 | "node_modules/@types/ws": { 115 | "version": "8.5.3", 116 | "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz", 117 | "integrity": "sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w==", 118 | "dependencies": { 119 | "@types/node": "*" 120 | } 121 | }, 122 | "node_modules/asn1": { 123 | "version": "0.2.6", 124 | "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", 125 | "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", 126 | "dependencies": { 127 | "safer-buffer": "~2.1.0" 128 | } 129 | }, 130 | "node_modules/base64-js": { 131 | "version": "1.5.1", 132 | "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", 133 | "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", 134 | "funding": [ 135 | { 136 | "type": "github", 137 | "url": "https://github.com/sponsors/feross" 138 | }, 139 | { 140 | "type": "patreon", 141 | "url": "https://www.patreon.com/feross" 142 | }, 143 | { 144 | "type": "consulting", 145 | "url": "https://feross.org/support" 146 | } 147 | ] 148 | }, 149 | "node_modules/bcrypt-pbkdf": { 150 | "version": "1.0.2", 151 | "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", 152 | "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", 153 | "dependencies": { 154 | "tweetnacl": "^0.14.3" 155 | } 156 | }, 157 | "node_modules/bl": { 158 | "version": "4.1.0", 159 | "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", 160 | "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", 161 | "dependencies": { 162 | "buffer": "^5.5.0", 163 | "inherits": "^2.0.4", 164 | "readable-stream": "^3.4.0" 165 | } 166 | }, 167 | "node_modules/buffer": { 168 | "version": "5.7.1", 169 | "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", 170 | "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", 171 | "funding": [ 172 | { 173 | "type": "github", 174 | "url": "https://github.com/sponsors/feross" 175 | }, 176 | { 177 | "type": "patreon", 178 | "url": "https://www.patreon.com/feross" 179 | }, 180 | { 181 | "type": "consulting", 182 | "url": "https://feross.org/support" 183 | } 184 | ], 185 | "dependencies": { 186 | "base64-js": "^1.3.1", 187 | "ieee754": "^1.1.13" 188 | } 189 | }, 190 | "node_modules/buildcheck": { 191 | "version": "0.0.3", 192 | "resolved": "https://registry.npmjs.org/buildcheck/-/buildcheck-0.0.3.tgz", 193 | "integrity": "sha512-pziaA+p/wdVImfcbsZLNF32EiWyujlQLwolMqUQE8xpKNOH7KmZQaY8sXN7DGOEzPAElo9QTaeNRfGnf3iOJbA==", 194 | "optional": true, 195 | "engines": { 196 | "node": ">=10.0.0" 197 | } 198 | }, 199 | "node_modules/busboy": { 200 | "version": "1.6.0", 201 | "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", 202 | "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", 203 | "dependencies": { 204 | "streamsearch": "^1.1.0" 205 | }, 206 | "engines": { 207 | "node": ">=10.16.0" 208 | } 209 | }, 210 | "node_modules/chownr": { 211 | "version": "1.1.4", 212 | "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", 213 | "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" 214 | }, 215 | "node_modules/core-util-is": { 216 | "version": "1.0.3", 217 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", 218 | "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" 219 | }, 220 | "node_modules/cpu-features": { 221 | "version": "0.0.4", 222 | "resolved": "https://registry.npmjs.org/cpu-features/-/cpu-features-0.0.4.tgz", 223 | "integrity": "sha512-fKiZ/zp1mUwQbnzb9IghXtHtDoTMtNeb8oYGx6kX2SYfhnG0HNdBEBIzB9b5KlXu5DQPhfy3mInbBxFcgwAr3A==", 224 | "hasInstallScript": true, 225 | "optional": true, 226 | "dependencies": { 227 | "buildcheck": "0.0.3", 228 | "nan": "^2.15.0" 229 | }, 230 | "engines": { 231 | "node": ">=10.0.0" 232 | } 233 | }, 234 | "node_modules/debug": { 235 | "version": "4.3.4", 236 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", 237 | "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", 238 | "dependencies": { 239 | "ms": "2.1.2" 240 | }, 241 | "engines": { 242 | "node": ">=6.0" 243 | }, 244 | "peerDependenciesMeta": { 245 | "supports-color": { 246 | "optional": true 247 | } 248 | } 249 | }, 250 | "node_modules/discord-api-types": { 251 | "version": "0.37.24", 252 | "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.24.tgz", 253 | "integrity": "sha512-1+Fb4huJCihdbkJLcq2p7nBmtlmAryNwjefT8wwJnL8c7bc7WA87Oaa5mbLe96QvZyfwnwRCDX40H0HhcVV50g==" 254 | }, 255 | "node_modules/discord.js": { 256 | "version": "14.7.1", 257 | "resolved": "https://registry.npmjs.org/discord.js/-/discord.js-14.7.1.tgz", 258 | "integrity": "sha512-1FECvqJJjjeYcjSm0IGMnPxLqja/pmG1B0W2l3lUY2Gi4KXiyTeQmU1IxWcbXHn2k+ytP587mMWqva2IA87EbA==", 259 | "dependencies": { 260 | "@discordjs/builders": "^1.4.0", 261 | "@discordjs/collection": "^1.3.0", 262 | "@discordjs/rest": "^1.4.0", 263 | "@discordjs/util": "^0.1.0", 264 | "@sapphire/snowflake": "^3.2.2", 265 | "@types/ws": "^8.5.3", 266 | "discord-api-types": "^0.37.20", 267 | "fast-deep-equal": "^3.1.3", 268 | "lodash.snakecase": "^4.1.1", 269 | "tslib": "^2.4.1", 270 | "undici": "^5.13.0", 271 | "ws": "^8.11.0" 272 | }, 273 | "engines": { 274 | "node": ">=16.9.0" 275 | } 276 | }, 277 | "node_modules/docker-modem": { 278 | "version": "3.0.6", 279 | "resolved": "https://registry.npmjs.org/docker-modem/-/docker-modem-3.0.6.tgz", 280 | "integrity": "sha512-h0Ow21gclbYsZ3mkHDfsYNDqtRhXS8fXr51bU0qr1dxgTMJj0XufbzX+jhNOvA8KuEEzn6JbvLVhXyv+fny9Uw==", 281 | "dependencies": { 282 | "debug": "^4.1.1", 283 | "readable-stream": "^3.5.0", 284 | "split-ca": "^1.0.1", 285 | "ssh2": "^1.11.0" 286 | }, 287 | "engines": { 288 | "node": ">= 8.0" 289 | } 290 | }, 291 | "node_modules/dockerode": { 292 | "version": "3.3.4", 293 | "resolved": "https://registry.npmjs.org/dockerode/-/dockerode-3.3.4.tgz", 294 | "integrity": "sha512-3EUwuXnCU+RUlQEheDjmBE0B7q66PV9Rw5NiH1sXwINq0M9c5ERP9fxgkw36ZHOtzf4AGEEYySnkx/sACC9EgQ==", 295 | "dependencies": { 296 | "@balena/dockerignore": "^1.0.2", 297 | "docker-modem": "^3.0.0", 298 | "tar-fs": "~2.0.1" 299 | }, 300 | "engines": { 301 | "node": ">= 8.0" 302 | } 303 | }, 304 | "node_modules/dotenv": { 305 | "version": "16.0.3", 306 | "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz", 307 | "integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==", 308 | "engines": { 309 | "node": ">=12" 310 | } 311 | }, 312 | "node_modules/end-of-stream": { 313 | "version": "1.4.4", 314 | "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", 315 | "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", 316 | "dependencies": { 317 | "once": "^1.4.0" 318 | } 319 | }, 320 | "node_modules/fast-deep-equal": { 321 | "version": "3.1.3", 322 | "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", 323 | "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" 324 | }, 325 | "node_modules/file-type": { 326 | "version": "18.0.0", 327 | "resolved": "https://registry.npmjs.org/file-type/-/file-type-18.0.0.tgz", 328 | "integrity": "sha512-jjMwFpnW8PKofLE/4ohlhqwDk5k0NC6iy0UHAJFKoY1fQeGMN0GDdLgHQrvCbSpMwbqzoCZhRI5dETCZna5qVA==", 329 | "dependencies": { 330 | "readable-web-to-node-stream": "^3.0.2", 331 | "strtok3": "^7.0.0", 332 | "token-types": "^5.0.1" 333 | }, 334 | "engines": { 335 | "node": ">=14.16" 336 | }, 337 | "funding": { 338 | "url": "https://github.com/sindresorhus/file-type?sponsor=1" 339 | } 340 | }, 341 | "node_modules/fs-constants": { 342 | "version": "1.0.0", 343 | "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", 344 | "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" 345 | }, 346 | "node_modules/ieee754": { 347 | "version": "1.2.1", 348 | "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", 349 | "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", 350 | "funding": [ 351 | { 352 | "type": "github", 353 | "url": "https://github.com/sponsors/feross" 354 | }, 355 | { 356 | "type": "patreon", 357 | "url": "https://www.patreon.com/feross" 358 | }, 359 | { 360 | "type": "consulting", 361 | "url": "https://feross.org/support" 362 | } 363 | ] 364 | }, 365 | "node_modules/inherits": { 366 | "version": "2.0.4", 367 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 368 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 369 | }, 370 | "node_modules/isarray": { 371 | "version": "0.0.1", 372 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", 373 | "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==" 374 | }, 375 | "node_modules/jsonparse": { 376 | "version": "0.0.5", 377 | "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-0.0.5.tgz", 378 | "integrity": "sha512-fw7Q/8gFR8iSekUi9I+HqWIap6mywuoe7hQIg3buTVjuZgALKj4HAmm0X6f+TaL4c9NJbvyFQdaI2ppr5p6dnQ==", 379 | "engines": [ 380 | "node >= 0.2.0" 381 | ] 382 | }, 383 | "node_modules/JSONStream": { 384 | "version": "0.10.0", 385 | "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-0.10.0.tgz", 386 | "integrity": "sha512-8XbSFFd43EG+1thjLNFIzCBlwXti0yKa7L+ak/f0T/pkC+31b7G41DXL/JzYpAoYWZ2eCPiu4IIqzijM8N0a/w==", 387 | "dependencies": { 388 | "jsonparse": "0.0.5", 389 | "through": ">=2.2.7 <3" 390 | }, 391 | "bin": { 392 | "JSONStream": "index.js" 393 | }, 394 | "engines": { 395 | "node": "*" 396 | } 397 | }, 398 | "node_modules/lodash": { 399 | "version": "4.17.21", 400 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", 401 | "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" 402 | }, 403 | "node_modules/lodash.snakecase": { 404 | "version": "4.1.1", 405 | "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz", 406 | "integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==" 407 | }, 408 | "node_modules/memorystream": { 409 | "version": "0.3.1", 410 | "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", 411 | "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==", 412 | "engines": { 413 | "node": ">= 0.10.0" 414 | } 415 | }, 416 | "node_modules/mkdirp-classic": { 417 | "version": "0.5.3", 418 | "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", 419 | "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==" 420 | }, 421 | "node_modules/ms": { 422 | "version": "2.1.2", 423 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 424 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" 425 | }, 426 | "node_modules/nan": { 427 | "version": "2.17.0", 428 | "resolved": "https://registry.npmjs.org/nan/-/nan-2.17.0.tgz", 429 | "integrity": "sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ==", 430 | "optional": true 431 | }, 432 | "node_modules/node-docker-api": { 433 | "version": "1.1.22", 434 | "resolved": "https://registry.npmjs.org/node-docker-api/-/node-docker-api-1.1.22.tgz", 435 | "integrity": "sha512-8xfOiuLDJQw+l58i66lUNQhRhS5fAExqQbLolmyqMucrsDON7k7eLMIHphcBwwB7utwCHCQkcp73gSAmzSiAiw==", 436 | "dependencies": { 437 | "docker-modem": "^0.3.1", 438 | "memorystream": "^0.3.1" 439 | } 440 | }, 441 | "node_modules/node-docker-api/node_modules/debug": { 442 | "version": "2.6.9", 443 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 444 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 445 | "dependencies": { 446 | "ms": "2.0.0" 447 | } 448 | }, 449 | "node_modules/node-docker-api/node_modules/docker-modem": { 450 | "version": "0.3.7", 451 | "resolved": "https://registry.npmjs.org/docker-modem/-/docker-modem-0.3.7.tgz", 452 | "integrity": "sha512-4Xn4ZVtc/2DEFtxY04lOVeF7yvxwXGVo0sN8FKRBnLhBcwQ78Hb56j+Z5yAXXUhoweVhzGeBeGWahS+af0/mcg==", 453 | "dependencies": { 454 | "debug": "^2.6.0", 455 | "JSONStream": "0.10.0", 456 | "readable-stream": "~1.0.26-4", 457 | "split-ca": "^1.0.0" 458 | }, 459 | "engines": { 460 | "node": ">= 0.8" 461 | } 462 | }, 463 | "node_modules/node-docker-api/node_modules/ms": { 464 | "version": "2.0.0", 465 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 466 | "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" 467 | }, 468 | "node_modules/node-docker-api/node_modules/readable-stream": { 469 | "version": "1.0.34", 470 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", 471 | "integrity": "sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==", 472 | "dependencies": { 473 | "core-util-is": "~1.0.0", 474 | "inherits": "~2.0.1", 475 | "isarray": "0.0.1", 476 | "string_decoder": "~0.10.x" 477 | } 478 | }, 479 | "node_modules/node-docker-api/node_modules/string_decoder": { 480 | "version": "0.10.31", 481 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", 482 | "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==" 483 | }, 484 | "node_modules/once": { 485 | "version": "1.4.0", 486 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 487 | "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", 488 | "dependencies": { 489 | "wrappy": "1" 490 | } 491 | }, 492 | "node_modules/peek-readable": { 493 | "version": "5.0.0", 494 | "resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-5.0.0.tgz", 495 | "integrity": "sha512-YtCKvLUOvwtMGmrniQPdO7MwPjgkFBtFIrmfSbYmYuq3tKDV/mcfAhBth1+C3ru7uXIZasc/pHnb+YDYNkkj4A==", 496 | "engines": { 497 | "node": ">=14.16" 498 | }, 499 | "funding": { 500 | "type": "github", 501 | "url": "https://github.com/sponsors/Borewit" 502 | } 503 | }, 504 | "node_modules/pump": { 505 | "version": "3.0.0", 506 | "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", 507 | "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", 508 | "dependencies": { 509 | "end-of-stream": "^1.1.0", 510 | "once": "^1.3.1" 511 | } 512 | }, 513 | "node_modules/readable-stream": { 514 | "version": "3.6.0", 515 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", 516 | "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", 517 | "dependencies": { 518 | "inherits": "^2.0.3", 519 | "string_decoder": "^1.1.1", 520 | "util-deprecate": "^1.0.1" 521 | }, 522 | "engines": { 523 | "node": ">= 6" 524 | } 525 | }, 526 | "node_modules/readable-web-to-node-stream": { 527 | "version": "3.0.2", 528 | "resolved": "https://registry.npmjs.org/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.2.tgz", 529 | "integrity": "sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw==", 530 | "dependencies": { 531 | "readable-stream": "^3.6.0" 532 | }, 533 | "engines": { 534 | "node": ">=8" 535 | }, 536 | "funding": { 537 | "type": "github", 538 | "url": "https://github.com/sponsors/Borewit" 539 | } 540 | }, 541 | "node_modules/safe-buffer": { 542 | "version": "5.2.1", 543 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 544 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", 545 | "funding": [ 546 | { 547 | "type": "github", 548 | "url": "https://github.com/sponsors/feross" 549 | }, 550 | { 551 | "type": "patreon", 552 | "url": "https://www.patreon.com/feross" 553 | }, 554 | { 555 | "type": "consulting", 556 | "url": "https://feross.org/support" 557 | } 558 | ] 559 | }, 560 | "node_modules/safer-buffer": { 561 | "version": "2.1.2", 562 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 563 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 564 | }, 565 | "node_modules/split-ca": { 566 | "version": "1.0.1", 567 | "resolved": "https://registry.npmjs.org/split-ca/-/split-ca-1.0.1.tgz", 568 | "integrity": "sha512-Q5thBSxp5t8WPTTJQS59LrGqOZqOsrhDGDVm8azCqIBjSBd7nd9o2PM+mDulQQkh8h//4U6hFZnc/mul8t5pWQ==" 569 | }, 570 | "node_modules/ssh2": { 571 | "version": "1.11.0", 572 | "resolved": "https://registry.npmjs.org/ssh2/-/ssh2-1.11.0.tgz", 573 | "integrity": "sha512-nfg0wZWGSsfUe/IBJkXVll3PEZ//YH2guww+mP88gTpuSU4FtZN7zu9JoeTGOyCNx2dTDtT9fOpWwlzyj4uOOw==", 574 | "hasInstallScript": true, 575 | "dependencies": { 576 | "asn1": "^0.2.4", 577 | "bcrypt-pbkdf": "^1.0.2" 578 | }, 579 | "engines": { 580 | "node": ">=10.16.0" 581 | }, 582 | "optionalDependencies": { 583 | "cpu-features": "~0.0.4", 584 | "nan": "^2.16.0" 585 | } 586 | }, 587 | "node_modules/streamsearch": { 588 | "version": "1.1.0", 589 | "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", 590 | "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", 591 | "engines": { 592 | "node": ">=10.0.0" 593 | } 594 | }, 595 | "node_modules/string_decoder": { 596 | "version": "1.3.0", 597 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", 598 | "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", 599 | "dependencies": { 600 | "safe-buffer": "~5.2.0" 601 | } 602 | }, 603 | "node_modules/strtok3": { 604 | "version": "7.0.0", 605 | "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-7.0.0.tgz", 606 | "integrity": "sha512-pQ+V+nYQdC5H3Q7qBZAz/MO6lwGhoC2gOAjuouGf/VO0m7vQRh8QNMl2Uf6SwAtzZ9bOw3UIeBukEGNJl5dtXQ==", 607 | "dependencies": { 608 | "@tokenizer/token": "^0.3.0", 609 | "peek-readable": "^5.0.0" 610 | }, 611 | "engines": { 612 | "node": ">=14.16" 613 | }, 614 | "funding": { 615 | "type": "github", 616 | "url": "https://github.com/sponsors/Borewit" 617 | } 618 | }, 619 | "node_modules/tar-fs": { 620 | "version": "2.0.1", 621 | "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.0.1.tgz", 622 | "integrity": "sha512-6tzWDMeroL87uF/+lin46k+Q+46rAJ0SyPGz7OW7wTgblI273hsBqk2C1j0/xNadNLKDTUL9BukSjB7cwgmlPA==", 623 | "dependencies": { 624 | "chownr": "^1.1.1", 625 | "mkdirp-classic": "^0.5.2", 626 | "pump": "^3.0.0", 627 | "tar-stream": "^2.0.0" 628 | } 629 | }, 630 | "node_modules/tar-stream": { 631 | "version": "2.2.0", 632 | "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", 633 | "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", 634 | "dependencies": { 635 | "bl": "^4.0.3", 636 | "end-of-stream": "^1.4.1", 637 | "fs-constants": "^1.0.0", 638 | "inherits": "^2.0.3", 639 | "readable-stream": "^3.1.1" 640 | }, 641 | "engines": { 642 | "node": ">=6" 643 | } 644 | }, 645 | "node_modules/through": { 646 | "version": "2.3.8", 647 | "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", 648 | "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==" 649 | }, 650 | "node_modules/token-types": { 651 | "version": "5.0.1", 652 | "resolved": "https://registry.npmjs.org/token-types/-/token-types-5.0.1.tgz", 653 | "integrity": "sha512-Y2fmSnZjQdDb9W4w4r1tswlMHylzWIeOKpx0aZH9BgGtACHhrk3OkT52AzwcuqTRBZtvvnTjDBh8eynMulu8Vg==", 654 | "dependencies": { 655 | "@tokenizer/token": "^0.3.0", 656 | "ieee754": "^1.2.1" 657 | }, 658 | "engines": { 659 | "node": ">=14.16" 660 | }, 661 | "funding": { 662 | "type": "github", 663 | "url": "https://github.com/sponsors/Borewit" 664 | } 665 | }, 666 | "node_modules/ts-mixer": { 667 | "version": "6.0.2", 668 | "resolved": "https://registry.npmjs.org/ts-mixer/-/ts-mixer-6.0.2.tgz", 669 | "integrity": "sha512-zvHx3VM83m2WYCE8XL99uaM7mFwYSkjR2OZti98fabHrwkjsCvgwChda5xctein3xGOyaQhtTeDq/1H/GNvF3A==" 670 | }, 671 | "node_modules/tslib": { 672 | "version": "2.4.1", 673 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", 674 | "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==" 675 | }, 676 | "node_modules/tweetnacl": { 677 | "version": "0.14.5", 678 | "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", 679 | "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==" 680 | }, 681 | "node_modules/undici": { 682 | "version": "5.14.0", 683 | "resolved": "https://registry.npmjs.org/undici/-/undici-5.14.0.tgz", 684 | "integrity": "sha512-yJlHYw6yXPPsuOH0x2Ib1Km61vu4hLiRRQoafs+WUgX1vO64vgnxiCEN9dpIrhZyHFsai3F0AEj4P9zy19enEQ==", 685 | "dependencies": { 686 | "busboy": "^1.6.0" 687 | }, 688 | "engines": { 689 | "node": ">=12.18" 690 | } 691 | }, 692 | "node_modules/util-deprecate": { 693 | "version": "1.0.2", 694 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 695 | "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" 696 | }, 697 | "node_modules/wrappy": { 698 | "version": "1.0.2", 699 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 700 | "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" 701 | }, 702 | "node_modules/ws": { 703 | "version": "8.11.0", 704 | "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", 705 | "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", 706 | "engines": { 707 | "node": ">=10.0.0" 708 | }, 709 | "peerDependencies": { 710 | "bufferutil": "^4.0.1", 711 | "utf-8-validate": "^5.0.2" 712 | }, 713 | "peerDependenciesMeta": { 714 | "bufferutil": { 715 | "optional": true 716 | }, 717 | "utf-8-validate": { 718 | "optional": true 719 | } 720 | } 721 | } 722 | } 723 | } 724 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "server-bot", 3 | "version": "1.0.0", 4 | "description": "Discord bot to remotely monitor and control a docker based server", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "nodemon index.js" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/allenrkeen/server-bot.git" 12 | }, 13 | "keywords": [ 14 | "discord", 15 | "docker", 16 | "linux", 17 | "selfhost" 18 | ], 19 | "author": "allenrkeen", 20 | "license": "MIT", 21 | "bugs": { 22 | "url": "https://github.com/allenrkeen/server-bot/issues" 23 | }, 24 | "homepage": "https://github.com/allenrkeen/server-bot#readme", 25 | "dependencies": { 26 | "discord.js": "^14.7.1", 27 | "dotenv": "^16.0.3", 28 | "node-docker-api": "^1.1.22" 29 | } 30 | } 31 | --------------------------------------------------------------------------------