# This is a combination of 12 commits.

# This is the 1st commit message:

Initial commit
# This is the commit message #2:

added whole syswtem

becuz i forgot to keep up with the github

# This is the commit message #3:

hihi hiha

# This is the commit message #4:

added startServer.sh

# This is the commit message #5:

hihi hiha

# This is the commit message #6:

fixed some packages

# This is the commit message #7:

fixed every bug with the stupid oop

# This is the commit message #8:

Revert "fixed every bug with the stupid oop"

This reverts commit d2255863aa44e2504f9f2cec9e1ae5d1094cb9cd.

# This is the commit message #9:

added prevention of breaking the system by manually filling in the project id

# This is the commit message #10:

fixed bug when adding bot

# This is the commit message #11:

made user-id not just count up from 0

# This is the commit message #12:

fixed the issue where if you load project page it woulnt show the commands until you refresh the page
This commit is contained in:
Valentijn :)
2023-11-24 11:04:00 +01:00
committed by HerpieDerpie@example.com
commit 48a5a3576d
20 changed files with 3712 additions and 0 deletions

130
.gitignore vendored Executable file
View File

@@ -0,0 +1,130 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Snowpack dependency directory (https://snowpack.dev/)
web_modules/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional stylelint cache
.stylelintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# Next.js build output
.next
out
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# vuepress v2.x temp and cache directory
.temp
.cache
# Docusaurus cache and generated files
.docusaurus
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*

2
README.md Executable file
View File

@@ -0,0 +1,2 @@
# PS-Discord-BotBuilder
a wesbite to make discord bots

55
example_bot/bot.js Executable file
View File

@@ -0,0 +1,55 @@
const { Client, Collection, Events, GatewayIntentBits } = require('discord.js');
const { updateCommands } = require("./updateCommands");
const { token } = require('./config.json');
const fs = require('node:fs');
const path = require('node:path');
const client = new Client({ intents: [GatewayIntentBits.Guilds] });
updateCommands();
/** REGISTER COMMANDS **/
client.commands = new Collection();
const foldersPath = path.join(__dirname, 'commands');
const commandFolders = fs.readdirSync(foldersPath);
for (const folder of commandFolders) {
const commandsPath = path.join(foldersPath, folder);
const commandFiles = fs.readdirSync(commandsPath).filter(file => file.endsWith('.js'));
for (const file of commandFiles) {
const filePath = path.join(commandsPath, file);
const command = require(filePath);
if ('data' in command && 'execute' in command) {
client.commands.set(command.data.name, command);
} else {
console.log(`[WARNING] The command at ${filePath} is missing a required "data" or "execute" property.`);
}
}
}
/** SLASH COMMAND HANDLER **/
client.on(Events.InteractionCreate, async interaction => {
if (interaction.isChatInputCommand()) {
const command = client.commands.get(interaction.commandName);
if (command) {
try {
await command.execute(interaction);
} catch (error) {
console.error(error);
if (!(interaction.replied || interaction.deferred)) {
await interaction.reply({ content: 'There was an error while executing this command!', ephemeral: true });
}
}
} else {
console.error(`No command matching ${interaction.commandName} was found.`);
}
}
});
/** READY SEQUENCE **/
client.once(Events.ClientReady, c => {
console.log(`Ready! Logged in as ${c.user.tag}`);
});
client.login(token);

269
example_bot/package-lock.json generated Executable file
View File

@@ -0,0 +1,269 @@
{
"name": "example_bot",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"dependencies": {
"discord.js": "^14.14.1"
}
},
"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/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/@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.0",
"resolved": "https://registry.npmjs.org/@sapphire/async-queue/-/async-queue-1.5.0.tgz",
"integrity": "sha512-JkLdIsP8fPAdh9ZZjrbHWR/+mZj0wvKS5ICibcLrRI1j84UmLMshx5n9QmL8b95d4onJ2xxiyugTgSAX7AalmA==",
"engines": {
"node": ">=v14.0.0",
"npm": ">=7.0.0"
}
},
"node_modules/@sapphire/shapeshift": {
"version": "3.9.3",
"resolved": "https://registry.npmjs.org/@sapphire/shapeshift/-/shapeshift-3.9.3.tgz",
"integrity": "sha512-WzKJSwDYloSkHoBbE8rkRW8UNKJiSRJ/P8NqJ5iVq7U2Yr/kriIBx2hW+wj2Z5e5EnXL1hgYomgaFsdK6b+zqQ==",
"dependencies": {
"fast-deep-equal": "^3.1.3",
"lodash": "^4.17.21"
},
"engines": {
"node": ">=v14.0.0",
"npm": ">=7.0.0"
}
},
"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/@types/node": {
"version": "20.10.1",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.1.tgz",
"integrity": "sha512-T2qwhjWwGH81vUEx4EXmBKsTJRXFXNZTL4v0gi01+zyBmCwzE6TyHszqX01m+QHTEq+EZNo13NeJIdEqf+Myrg==",
"dependencies": {
"undici-types": "~5.26.4"
}
},
"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/@vladfrangu/async_event_emitter": {
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/@vladfrangu/async_event_emitter/-/async_event_emitter-2.2.2.tgz",
"integrity": "sha512-HIzRG7sy88UZjBJamssEczH5q7t5+axva19UbZLO6u0ySbYPrwzWiXBcC0WuHyhKKoeCyneH+FvYzKQq/zTtkQ==",
"engines": {
"node": ">=v14.0.0",
"npm": ">=7.0.0"
}
},
"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/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/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.5.0",
"resolved": "https://registry.npmjs.org/magic-bytes.js/-/magic-bytes.js-1.5.0.tgz",
"integrity": "sha512-wJkXvutRbNWcc37tt5j1HyOK1nosspdh3dj6LUYYAvF6JYNqs53IfRvK9oEpcwiDA1NdoIi64yAMfdivPeVAyw=="
},
"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/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/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
}
}
}
}
}

5
example_bot/package.json Executable file
View File

@@ -0,0 +1,5 @@
{
"dependencies": {
"discord.js": "^14.14.1"
}
}

82
example_bot/updateCommands.js Executable file
View File

@@ -0,0 +1,82 @@
const { REST, Routes } = require('discord.js');
const { clientId, token } = require('./config.json');
const fs = require('node:fs');
const path = require('node:path');
const generateCommandFile = (command) => {
return `const { SlashCommandBuilder } = require('discord.js');
module.exports = {
data: new SlashCommandBuilder()
.setName('${command.name}')
.setDescription('${command.description}'),
async execute(interaction) {
await interaction.reply('${command.response}');
},
};
`;
};
const updateCommands = () => {
const commands = [];
const foldersPath = path.join(__dirname, 'commands');
const utilityFolderPath = path.join(foldersPath, 'utility');
// Delete all files from ./commands/utility/ before creating new ones
fs.readdirSync(utilityFolderPath).forEach((file) => {
const filePath = path.join(utilityFolderPath, file);
fs.unlinkSync(filePath);
console.log(`Deleted ${file}`);
});
// CREAYE ALL THE COMMAND FILES HERE (OVERWRITE THEM IF THEY ALREADY EXSIST)
const { commands: jsonCommands } = require('./config.json');
for (const command of jsonCommands) {
const commandFileName = `${command.name}.js`;
const commandFilePath = path.join(foldersPath, `utility/${commandFileName}`);
if (!fs.existsSync(commandFilePath)) {
// If the command file doesn't exist, create it
fs.writeFileSync(commandFilePath, generateCommandFile(command));
console.log(`Created ${commandFileName}`);
}
}
for (const folder of fs.readdirSync(foldersPath)) {
const commandsPath = path.join(foldersPath, folder);
const commandFiles = fs.readdirSync(commandsPath).filter(file => file.endsWith('.js'));
for (const file of commandFiles) {
const filePath = path.join(commandsPath, file);
const command = require(filePath);
if ('data' in command && 'execute' in command) {
commands.push(command.data.toJSON());
} else {
console.log(`[WARNING] The command at ${filePath} is missing a required "data" or "execute" property.`);
}
}
}
const rest = new REST().setToken(token);
(async () => {
try {
console.log(`⚙️ Started refreshing ${commands.length} application (/) commands.`);
const data = await rest.put(
Routes.applicationCommands(clientId),
{ body: commands },
);
console.log(`✅ Successfully reloaded ${data.length} application (/) commands.`);
} catch (error) {
console.error(`${error}`);
}
})();
}
module.exports = {
updateCommands
}

13
hash.js Executable file
View File

@@ -0,0 +1,13 @@
const bcrypt = require('bcrypt');
// Password to be hashed
const passwordToHash = 'password_to_hash';
// Hashing the password with bcrypt
bcrypt.hash(passwordToHash, 10, (err, hash) => {
if (err) {
console.error('Error hashing password:', err);
} else {
console.log('Hashed password:', hash);
}
});

22
httpdocs/dashboard.html Executable file
View File

@@ -0,0 +1,22 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Dashboard</title>
<link rel="stylesheet" href="/public.css">
<link rel="stylesheet" href="/dashboard.css">
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@10"></script>
<script src="bots.js" defer></script>
</head>
<body>
<div id="main">
<div id="container">
<h1>Choose the bot you want to edit</h1>
<div id="grid" class="bot-list">
</div>
</div>
</div>
</body>
</html>

24
httpdocs/login.html Executable file
View File

@@ -0,0 +1,24 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="/login.css">
<link rel="stylesheet" href="/public.css">
<title>Login</title>
</head>
<body>
<div class="login-container">
<h2>Login to DiscordBotBuilder</h2>
<form action="/" method="POST">
<label for="username">Username:</label>
<input type="text" id="username" name="username" required>
<label for="password">Password:</label>
<input type="password" id="password" name="password" required>
<button type="submit">Login</button>
</form>
</div>
</body>
</html>

26
httpdocs/project.html Executable file
View File

@@ -0,0 +1,26 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Project</title>
<link rel="stylesheet" href="/public.css">
<link rel="stylesheet" href="/project.css">
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@10"></script>
<script src="/project.js" defer></script>
</head>
<body>
<div id="main">
<div id="container" onload="loadData()">
<h1 id="title"></h1>
<button id="editProjectName" onclick="editProjectName()">Edit Name</button>
<button id="exportProject" onclick="exportProject()">Export Project</button>
<h2 id="description"></h2><br>
<h3 id="section-title">Commands:</h3>
<div id="grid" class="bot-list">
</div>
</div>
</div>
</body>
</html>

2089
package-lock.json generated Executable file

File diff suppressed because it is too large Load Diff

23
package.json Executable file
View File

@@ -0,0 +1,23 @@
{
"name": "crud-nodejs",
"version": "1.0.0",
"description": "",
"main": "server.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "node server.js"
},
"author": "",
"license": "ISC",
"dependencies": {
"archiver": "^6.0.1",
"bcrypt": "^5.1.1",
"body-parser": "^1.20.2",
"express": "^4.18.2",
"express-session": "^1.17.3",
"fs-extra": "^11.2.0",
"mysql": "^2.18.1",
"mysql2": "^3.6.5",
"nodemon": "^3.0.1"
}
}

109
scripts/bots.js Executable file
View File

@@ -0,0 +1,109 @@
function createNewBot() {
Swal.fire({
title: 'Create Bot',
html:
'<label for="name">Name</label>' +
'<input id="name" class="swal2-input" placeholder="Project Name">' +
'<label for="id">Bot ID</label>' +
'<input id="id" class="swal2-input" placeholder="Bot ID">' +
'<label for="token">Token</label>' +
'<input id="token" class="swal2-input" placeholder="Bot Token">',
focusConfirm: false,
showCancelButton: true,
cancelButtonText: 'Cancel',
confirmButtonText: 'Create',
preConfirm: () => {
const name = Swal.getPopup().querySelector('#name').value;
const token = Swal.getPopup().querySelector('#token').value;
const clientID = Swal.getPopup().querySelector('#id').value;
// Check if both name and token are filled
if (!name || !token || !clientID) {
Swal.showValidationMessage('Please fill in both the Name and Token fields');
return false; // Prevent closing the modal
}
// Perform the POST request to /create-bot
return fetch('/create-bot', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
name: name,
token: token,
clientId: clientID,
}),
})
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
})
.then(data => {
console.log(data);
Swal.fire('Bot Created!', 'Your bot has been successfully created.', 'success');
refreshList();
})
.catch(error => {
console.error('There was a problem with the fetch operation:', error);
Swal.fire('Error', 'There was an error creating the bot.', 'error');
});
},
});
}
function refreshList() {
let xhr = new XMLHttpRequest();
xhr.open('GET', '/fetch-bots', true);
xhr.send();
xhr.onload = function () {
if (xhr.status == 200) {
let jsonResponse = JSON.parse(xhr.responseText);
const gridContainer = document.getElementById("grid");
gridContainer.innerHTML = '';
const specialButton = document.createElement('div');
specialButton.classList.add('grid-btn');
specialButton.id = 'special-button';
const button = document.createElement('button');
button.innerText = 'NEW BOT';
button.id = 'add-bot-button';
button.onclick = createNewBot;
specialButton.appendChild(button);
gridContainer.appendChild(specialButton);
jsonResponse.forEach(bot => {
const gridItem = document.createElement('div');
gridItem.classList.add('grid-btn');
const button = document.createElement('button');
button.innerText = bot.name;
button.dataset.projectID=bot.id;
button.addEventListener("click", (event)=>{
window.location.href=`/projects/${event.target.dataset.projectID}/`
});
gridItem.appendChild(button);
gridContainer.appendChild(gridItem);
});
} else {
console.error('Request failed. Status: ' + xhr.status);
}
};
}
document.addEventListener('DOMContentLoaded', function () {
refreshList();
});

266
scripts/project.js Executable file
View File

@@ -0,0 +1,266 @@
function getProjectId() {
let currentPage = window.location.href;
return parseInt(currentPage.split('/projects/')[1].split('/')[0]);
}
function editProjectName(){
Swal.fire({
title: 'Enter Project Name',
html:
'<input id="projectName" class="swal2-input" placeholder="Project Name">',
focusConfirm: false,
preConfirm: () => {
const name = Swal.getPopup().querySelector('#projectName').value;
// Check if both name and token are filled
if (!name) {
Swal.showValidationMessage('Please fill in all the fields');
return false; // Prevent closing the modal
}
// Perform the POST request to /create-bot
return fetch('/rename-project', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
name: name,
id: getProjectId()
}),
})
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
})
.then(data => {
Swal.fire('Project Rename!', 'Your project has been successfully been renamed.', 'success').then(() => {
window.location.reload();
});
})
.catch(error => {
console.error('There was a problem with the fetch operation:', error);
Swal.fire('Error', 'There was an error creating the bot.', 'error');
});
},
})
}
function exportProject() {
Swal.fire({
title: "Exporting...",
text: "Your project export is in progress.",
icon: "info",
showConfirmButton: false,
allowOutsideClick: false,
allowEscapeKey: false
});
fetch(`/export?id=${getProjectId()}`, { method: 'POST' })
.then(response => {
if (!response.ok) {
throw new Error(`Request failed. Status: ${response.status}`);
}
return response.blob();
})
.then(blob => {
// Create a link element
const link = document.createElement('a');
link.href = window.URL.createObjectURL(blob);
// Set the download attribute and trigger a click
link.download = `bot_builds_${getProjectId()}.zip`;
link.click();
// Clean up
window.URL.revokeObjectURL(link.href);
Swal.close();
})
.then(() => {
Swal.fire({
title: "Exported!",
text: "Your project has been exported.",
icon: "success"
});
loadData();
})
.catch(error => {
console.error('Error:', error);
Swal.fire({
title: "Export Failed",
text: "There was an error exporting your project.",
icon: "error"
});
});
}
function createNewCommand() {
Swal.fire({
title: 'Create Command',
html:
'<label for="name">Command Name</label>' +
'<input id="name" class="swal2-input" placeholder="Command Name">' +
'<label for="description">Description</label>' +
'<input id="description" class="swal2-input" placeholder="Command Description">'+
'<label for="response">Command Response</label>' +
'<input id="response" class="swal2-input" placeholder="Command Response">',
focusConfirm: false,
showCancelButton: true,
cancelButtonText: 'Cancel',
confirmButtonText: 'Create',
preConfirm: () => {
const name = Swal.getPopup().querySelector('#name').value;
const description = Swal.getPopup().querySelector('#description').value;
const response = Swal.getPopup().querySelector('#response').value;
// Check if both name and token are filled
if (!name || !description || !response) {
Swal.showValidationMessage('Please fill in all the fields');
return false; // Prevent closing the modal
}
// Perform the POST request to /create-bot
return fetch('/create-command', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
name: name,
description: description,
response: response,
projectId: getProjectId()
}),
})
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
})
.then(data => {
console.log(data);
Swal.fire('Command Created!', 'Your command has been successfully created.', 'success');
loadData();
})
.catch(error => {
console.error('There was a problem with the fetch operation:', error);
Swal.fire('Error', 'There was an error creating the bot.', 'error');
});
},
});
}
function deleteCommand (event) {
const commandId=event.target.dataset.commandId;
const projectId = getProjectId();
Swal.fire({
title: "Are you sure you want to delete this command?",
text: "You won't be able to revert this!",
icon: "warning",
showCancelButton: true,
confirmButtonColor: "#3085d6",
cancelButtonColor: "#d33",
confirmButtonText: "Yes, delete it!"
}).then((result) => {
if (result.isConfirmed) {
let xhr = new XMLHttpRequest();
xhr.open("POST", `/delete-command?commandId=${commandId}&projectId=${projectId}`);
xhr.send();
xhr.onload = () => {
if (xhr.status == 200){
Swal.fire({
title: "Deleted!",
text: "Your command has been deleted.",
icon: "success"
})
loadData();
} else {
console.error('Request failed. Status: ' + xhr.status);
}
}
}
});
}
function loadData() {
let projectId = getProjectId()
let xhr = new XMLHttpRequest();
xhr.open('GET', `/fetch-project-data?id=${projectId}`, true);
xhr.send();
xhr.onload = () => {
if (xhr.status == 200){
let jsonResponse = JSON.parse(xhr.responseText);
if (jsonResponse != {}){
document.querySelector("#title").innerHTML = jsonResponse.projectName.toUpperCase();
document.querySelector("#description").innerHTML = "Project Id: "+jsonResponse.projectId;
}
} else {
console.error('Request failed. Status: ' + xhr.status);
}
}
let xhr2 = new XMLHttpRequest();
xhr2.open('GET', `/fetch-commands?id=${projectId}`, true);
xhr2.send();
xhr2.onload = function () {
if (xhr2.status == 200) {
let jsonResponse = JSON.parse(xhr2.responseText);
console.log(jsonResponse)
const gridContainer = document.getElementById("grid");
gridContainer.innerHTML = '';
const specialButton = document.createElement('div');
specialButton.classList.add('grid-btn');
specialButton.id = 'special-button';
const button = document.createElement('button');
button.innerText = 'NEW COMMAND';
button.id = 'add-command-button';
button.onclick = createNewCommand;
specialButton.appendChild(button);
gridContainer.appendChild(specialButton);
jsonResponse.forEach(command => {
const gridItem = document.createElement('div');
gridItem.classList.add('grid-btn');
const button = document.createElement('button');
button.innerHTML = "<strong class='command-bold'>"+command.name+"</strong><br>"+command.description;
button.dataset.commandId=command.id;
button.dataset.commandResponse=command.response;
button.addEventListener("click", (event)=>{
deleteCommand(event);
});
gridItem.appendChild(button);
gridContainer.appendChild(gridItem);
});
} else {
console.error('Request failed. Status: ' + xhr2.status);
}
};
}
document.addEventListener('DOMContentLoaded', function () {
loadData();
});

390
server.js Executable file
View File

@@ -0,0 +1,390 @@
/*
Importing all the required packages
*/
const express = require('express');
const session = require('express-session');
const bodyParser = require('body-parser');
const mysql = require('mysql');
const bcrypt = require('bcrypt');
const fs = require('fs-extra');
const path = require('path');
const archiver = require('archiver');
/*
Creating some handy constants
*/
const documentRoot = `${__dirname}/httpdocs`
const webserverPort = 3000
/**
* Creates a MySQL connection.
* @returns {Object} MySQL connection object
*/
const createConnection = () => {
return mysql.createConnection({
host: "localhost",
user: "dcBB",
password: "DiscordBotBuilder",
database: "PS_DISCORD_BOT_BUILDER",
connectTimeout: 20000,
});
};
/**
* Setting up the express webserver
*
*/
const app = express()
app.use(express.static("styles"))
app.use(express.static("scripts"))
app.use(session({secret: 'secret',resave: true,saveUninitialized: true}));
app.use( bodyParser.json() );
app.use(express.json());
app.use(bodyParser.urlencoded({extended: true}));
app.use(express.urlencoded({extended: true}));
/*
Setting up the login system
*/
const requireLogin = (req, res, next) => {
if (!req.session.userId) {
res.redirect('/');
} else {
next();
}
};
/*
Setting up all the website pages
*/
app.get("/", (req, res) => {
res.sendFile(`${documentRoot}/login.html`);
})
app.get("/dashboard",requireLogin, (req, res) => {
res.sendFile(`${documentRoot}/dashboard.html`);
});
app.get("/projects/:projectId/", requireLogin, (req, res)=>{
const con = createConnection();
const sql = "SELECT MAX(project_id) as maxProjectID FROM Projects";
con.query(sql, (err, results) => {
if (err) throw err;
con.end();
const maxProjectID = results[0].maxProjectID;
if (parseInt(maxProjectID) < parseInt(req.params.projectId)) return res.redirect("/dashboard");
else {
res.sendFile(`${documentRoot}/project.html`);
}
});
})
/*
Setting up all the website posting pages.
*/
app.post("/", (req, res) => {
/**
* Login Validation
*/
const { username, password } = req.body;
const con = createConnection();
const sql = "SELECT * FROM Users WHERE username = ?";
con.query(sql, [username], (err, results) => {
if (err) {
console.error('Error executing login query:', err);
res.status(500).send('Internal Server Error');
return;
}
con.end();
if (results.length > 0) {
bcrypt.compare(password, results[0].password, (bcryptErr, isMatch) => {
if (bcryptErr) {
console.error('Error comparing passwords:', bcryptErr);
res.status(500).send('Internal Server Error');
return;
}
if (isMatch) {
req.session.userId = results[0].user_id;
res.redirect('/dashboard');
} else {
res.send('Invalid username or password');
}
});
} else {
res.send('Invalid username or password');
}
});
});
/*
Commands Grid
*/
app.get("/fetch-commands", requireLogin, (req, res)=> {
const projectId = req.query.id;
const userId = req.session.userId;
const con = createConnection();
const ownerCheck = "SELECT project_id FROM Projects WHERE project_id=? AND project_owner=?"
con.query(ownerCheck, [projectId, userId], (err, results)=>{
if (err) throw err;
if (results.length != 1)return res.json({});
const sql = "SELECT command_id, command_name, command_description, command_response FROM Commands WHERE project_id = ?";
con.query(sql, [projectId], (err, results) => {
if (err) throw err;
con.end();
let result = [];
for (let i = 0; i < results.length; i++){
result.push({"name": results[i]["command_name"], "description": results[i]["command_description"], "id": results[i]["command_id"], "response": results[i]["command_response"]});
}
res.json(result);
});
})
});
app.get("/fetch-project-data", requireLogin, (req, res)=>{
const projectId = req.query.id;
const userId = req.session.userId;
let response = {};
const con = createConnection();
const sql = "SELECT * FROM Projects WHERE project_id=? AND project_owner=?";
con.query(sql, [projectId, userId], (err, results)=>{
if (err) throw err;
if (results.length != 1) return res.json(response);
con.end();
response["projectName"] = results[0]["project_name"];
response["projectId"] = projectId;
res.json(response)
})
});
app.post("/delete-command", requireLogin, (req, res)=> {
const projectId = req.query.projectId;
const commandId = req.query.commandId;
const userId = req.session.userId;
const con = createConnection();
const ownerCheck = "SELECT project_id FROM Projects WHERE project_id=? AND project_owner=?"
con.query(ownerCheck, [projectId, userId], (err, results)=>{
if (err) throw err;
if (results.length != 1)return res.json({});
const sql = "DELETE FROM Commands WHERE command_id=?";
con.query(sql, [commandId], (err, results)=>{
if (err) throw err;
con.end();
res.status(200).send("true");
})
})
})
app.post("/create-command", requireLogin, (req, res)=> {
let name = req.body.name;
let description = req.body.description;
let response = req.body.response;
name = name.replace(" ", "-");
let projectId = req.body.projectId;
let owner = req.session.userId;
const con = createConnection();
const ownerCheck = "SELECT project_id FROM Projects WHERE project_id=? AND project_owner=?"
con.query(ownerCheck, [projectId, owner], (err, results)=>{
if (err) throw err;
if (results.length != 1)return;
const sql = "INSERT INTO Commands (project_id, command_name, command_description, command_response) VALUES (?,?,?,?)";
con.query(sql, [projectId, name, description, response], (err, results)=>{
if (err) throw err;
con.end();
res.status(200).send("true");
})
})
})
/*
Projects Grid
*/
app.get("/fetch-bots", requireLogin, (req, res)=> {
const sql = "SELECT project_name, project_id FROM Projects WHERE project_owner = ?";
const con = createConnection();
con.query(sql, [req.session.userId], (err, results) => {
if (err) throw err;
con.end();
let result = [];
for (let i = 0; i < results.length; i++){
result.push({"name": results[i]["project_name"], "id": results[i]["project_id"]});
}
res.json(result);
});
});
app.post("/create-bot", requireLogin, (req, res)=> {
let name = req.body.name;
let token = req.body.token;
let clientId = req.body.clientId;
let owner = req.session.userId;
if (name.length > 20)return;
let bot = {"name": name}
const con = createConnection();
var sql = "INSERT INTO Projects (project_name, project_owner, bot_token, bot_id) VALUES (?, ?, ?, ?)";
con.query(sql, [name, owner, token, clientId], function (err, result) {
if (err) throw err;
con.end();
});
res.send('true');
})
app.post("/rename-project", requireLogin, (req,res)=>{
let name = req.body.name;
let projectId = req.body.id;
let userId = req.session.userId;
const con = createConnection();
const ownerCheck = "SELECT project_id FROM Projects WHERE project_id=? AND project_owner=?"
con.query(ownerCheck, [projectId, userId], (err, results)=>{
if (err) throw err;
if (results.length != 1)return;
const sql = "UPDATE Projects SET project_name=? WHERE project_id=?";
con.query(sql, [name, projectId], (err, results)=>{
if (err) throw err;
con.end();
res.status(200).send("true");
})
})
})
app.post("/export", requireLogin, (req, res) => {
const projectId = req.query.id;
const userId = req.session.userId;
const con = createConnection();
const ownerCheck = "SELECT * FROM Projects WHERE project_id=? AND project_owner=?";
con.query(ownerCheck, [projectId, userId], (err, results) => {
if (err) throw err;
if (results.length !== 1) return res.json({});
const data = results[0];
const botToken = data.bot_token;
const clientId = data.bot_id;
con.query("SELECT command_id, command_name, command_description, command_response FROM Commands WHERE project_id = ?", [projectId], (err, result) => {
if (err) throw err;
con.end();
const commands = result.map(command => {
return {
name: command.command_name,
description: command.command_description,
response: command.command_response
};
});
const jsonContent = {
token: botToken,
clientId: clientId,
commands: commands
};
const botBuildsPath = `./bot_builds/${projectId}`;
// Create the directory if it doesn't exist
fs.ensureDirSync(botBuildsPath);
// Copy everything from "./example_bot" to "./botBuilds/{id}/"
const exampleBotPath = "./example_bot";
fs.copySync(exampleBotPath, botBuildsPath);
// Write JSON to a file in the new directory
const configFile = path.join(botBuildsPath, "config.json");
fs.writeFileSync(configFile, JSON.stringify(jsonContent, null, 2));
// Create a zip file
const outputZipPath = path.join(__dirname, `./bot_builds/${projectId}.zip`);
const outputZipStream = fs.createWriteStream(outputZipPath);
const archive = archiver('zip', {
zlib: { level: 9 }
});
outputZipStream.on('close', () => {
res.download(outputZipPath, (err) => {
if (err) throw err;
fs.removeSync(outputZipPath);
fs.removeSync(botBuildsPath);
});
});
archive.on('error', (err) => {
throw err;
});
archive.pipe(outputZipStream);
archive.directory(botBuildsPath, false);
archive.finalize();
});
});
});
/*
Starting up the server.
*/
app.listen(webserverPort, () => {
console.log(`Bot Builder Online at http://localhost:${webserverPort}/`)
})

2
startServer.sh Executable file
View File

@@ -0,0 +1,2 @@
#!/bin/bash
node server.js

57
styles/dashboard.css Executable file
View File

@@ -0,0 +1,57 @@
:root {
--mainMargin: 30px;
}
#main {
margin: var(--mainMargin);
width: calc(100% - var(--mainMargin)*2);
height: calc(100vh - var(--mainMargin)*2);
}
#container {
background: var(--dark-gray);
width: 100%;
height: 100%;
overflow: visible !important;
border-radius: 40px;
-webkit-box-shadow: 0px 0px 10px 10px var(--dark-steel);
-moz-box-shadow: 0px 0px 10px 10px var(--dark-steel);
box-shadow: 0px 0px 10px 10px var(--dark-steel);
display: flex;
justify-content: flex-start;
align-items: center;
flex-direction: column;
gap: 20px;
}
#grid {
width: 90%;
height: 90%;
display: flex;
justify-content: flex-start;
gap:5%;
flex-wrap: wrap;
}
#grid > * {
border-radius: 15px;
background-color: var(--light-gray);
width: 200px;
height: 200px;
}
#special-button {
background-color: green;
}
.grid-btn > button {
width: 100%;
height: 100%;
background: none;
border:none;
cursor:pointer;
}

52
styles/login.css Executable file
View File

@@ -0,0 +1,52 @@
body {
display: flex;
align-items: center;
justify-content: center;
height: 100vh;
background: var(--discord-blue);
}
.login-container {
background-color: var(--dark-gray);
border-radius: 8px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
padding: 30px;
text-align: center;
color: var(--light-gray);
width: 300px;
}
h2 {
margin-bottom: 20px;
}
form {
display: flex;
flex-direction: column;
}
label {
margin-bottom: 8px;
}
input {
padding: 10px;
margin-bottom: 16px;
border: 1px solid var(--discord-blue);
border-radius: 4px;
background-color: var(--dark-steel);
color: white;
}
button {
padding: 12px;
background-color: var(--discord-blue);
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
button:hover {
background-color: var(--medium-gray);
}

66
styles/project.css Executable file
View File

@@ -0,0 +1,66 @@
:root {
--mainMargin: 30px;
}
#main {
margin: var(--mainMargin);
width: calc(100% - var(--mainMargin)*2);
height: calc(100vh - var(--mainMargin)*2);
}
#container {
background: var(--dark-gray);
width: 100%;
height: 100%;
overflow: visible !important;
border-radius: 40px;
-webkit-box-shadow: 0px 0px 10px 10px var(--dark-steel);
-moz-box-shadow: 0px 0px 10px 10px var(--dark-steel);
box-shadow: 0px 0px 10px 10px var(--dark-steel);
display: flex;
justify-content: flex-start;
align-items: center;
flex-direction: column;
gap: 20px;
}
#grid {
width: 90%;
height: 90%;
display: flex;
justify-content: flex-start;
gap:5%;
flex-wrap: wrap;
}
#grid > * {
border-radius: 15px;
background-color: var(--light-gray);
width: 200px;
height: 200px;
}
#special-button {
background-color: green;
}
.grid-btn > button {
width: 100%;
height: 100%;
background: none;
border:none;
cursor:pointer;
}
.command-bold {
font-size: large;
}
#editProjectName,
#exportProject {
color: black !important;
}

30
styles/public.css Executable file
View File

@@ -0,0 +1,30 @@
:root {
--discord-blue: #7289da;
--dark-gray: #424549;
--medium-gray: #36393e;
--light-gray: #282b30;
--dark-steel: #1e2124;
}
* {
margin:0;
padding:0;
color: white;
}
body {
height: 100vh !important;
background: var(--discord-blue);
overflow: hidden;
}
#main {
height: 100%;
display: flex;
justify-content: center;
align-items: center;
}
.swal2-input {
color: black !important;
}