diff --git a/README.md b/README.md index 4538c8b..c5c10a4 100644 --- a/README.md +++ b/README.md @@ -8,14 +8,14 @@ Im going to make a To-do List for the features i want to implement for my Bot. 1. **Basic Commands** - [x] Add a `!help` command to display a list of available commands. - [x] Implement a `!ping` command to check bot responsiveness. - - [ **in progress** ] Add a `!about` command to describe the bot's purpose. + - [ **Only Text missing** ] Add a `!about` command to describe the bot's purpose. 2. **Role Management** - [ ] Set up a self-assignable role system (e.g., using reaction roles). - [ ] Allow admins to assign/remove roles with commands. 3. **Moderation Tools** - - [ ] Add commands to mute, kick, and ban users. + - [ ] Add commands to mute, x kick, and ban users. - [ ] Create an auto-moderation feature to delete spam or inappropriate content. - [ ] Implement a warning system with a strike counter. diff --git a/commands/db/test.js b/commands/db/test.js new file mode 100644 index 0000000..3adc425 --- /dev/null +++ b/commands/db/test.js @@ -0,0 +1,27 @@ +// exampleCommand.js +const { SlashCommandBuilder } = require('discord.js'); +const { connectToDatabase } = require('../../db.js'); + +module.exports = { + data: new SlashCommandBuilder() + .setName('example') + .setDescription('An example DB usage') + .addStringOption(option => + option + .setName('message') + .setDescription('string to store') + .setRequired(true)), + async execute(interaction) { + const dbClient = await connectToDatabase(); + const message = interaction.options.getString('message'); + + // Choose a DB + Collection + const db = dbClient.db('discord'); + const collection = db.collection('test'); + + // Example: Insert something + await collection.insertOne({ userId: interaction.user.id, message: message }); + + await interaction.reply('Successfully inserted your data into MongoDB.'); + }, +}; diff --git a/commands/moderation/mute.js b/commands/moderation/mute.js new file mode 100644 index 0000000..5d4df3b --- /dev/null +++ b/commands/moderation/mute.js @@ -0,0 +1,44 @@ +const { SlashCommandBuilder } = require("discord.js"); + +module.exports = { + data: new SlashCommandBuilder() + .setName("mute") + .setDescription("mute misbehaving people") + .addUserOption(option => + option + .setName('target') + .setDescription('The member to mute') + .setRequired(true)) + .addIntegerOption(option => + option + .setName('duration') + .setDescription('The duration of the Mute (Minutes)') + .setRequired(true)) + .addStringOption(option => + option + .setName('reason') + .setDescription('The reason of the mute')), + async execute(interaction) { + // Get the user and duration from the command options + const target = interaction.options.getUser('target'); + const duration = interaction.options.getInteger('duration'); + + // Convert duration to milliseconds + const durationMs = duration * 60 * 1000; + + try { + // Apply the timeout + await target.timeout(durationMs); + + await interaction.reply({ + content: `${target.tag} has been muted for ${duration} minutes.` + }); + } catch (error) { + console.error(error); + await interaction.reply({ + content: 'I was unable to mute the member.', + ephemeral: true, + }); + } + }, +}; \ No newline at end of file diff --git a/commands/moderation/temp_ban.js b/commands/moderation/temp_ban.js new file mode 100644 index 0000000..e69de29 diff --git a/commands/unique/quiz.js b/commands/unique/quiz.js new file mode 100644 index 0000000..6956d7f --- /dev/null +++ b/commands/unique/quiz.js @@ -0,0 +1,45 @@ +const quiz = require('./quiz.json'); +const {SlashCommandBuilder} = require("discord.js"); + +module.exports = { + data: new SlashCommandBuilder() + .setName("quiz") + .setDescription("Start a cool quiz"), + async execute(interaction) { + const item = quiz[Math.floor(Math.random() * quiz.length)]; + await interaction.reply({ + content: `**Quiz Time!**\n${item.question}\n\n_You have 30 seconds to answer!_`, + }); + + // Define a filter to check the correct answer + const collectorFilter = (response) => { + return ( + response.author.id === interaction.user.id && // Ensure it's the same user + item.answers.some( + (answer) => answer.toLowerCase() === response.content.toLowerCase() + ) + ); + }; + + try { + // Await messages in the same channel + const collected = await interaction.channel.awaitMessages({ + filter: collectorFilter, + max: 1, + time: 30000, // 30 seconds + errors: ['time'], + }); + + // If a message is collected, reply with the correct response + const correctAnswer = collected.first(); + await interaction.followUp( + `${correctAnswer.author} got the correct answer: **${correctAnswer.content}**! 🎉` + ); + } catch (err) { + // If no one answers in time + await interaction.followUp( + `⏳ Time's up! Nobody got the correct answer. The correct answers were: **${item.answers.join(', ')}**.` + ); + } + }, +}; \ No newline at end of file diff --git a/commands/unique/quiz.json b/commands/unique/quiz.json new file mode 100644 index 0000000..260f757 --- /dev/null +++ b/commands/unique/quiz.json @@ -0,0 +1,10 @@ +[ + { + "question": "What color is the sky?", + "answers": ["blue"] + }, + { + "question": "How many letters are there in the alphabet?", + "answers": ["26", "twenty-six", "twenty six", "twentysix"] + } +] diff --git a/commands/utility/about.js b/commands/utility/about.js index 7bcf4c1..c809cd3 100644 --- a/commands/utility/about.js +++ b/commands/utility/about.js @@ -1,13 +1,19 @@ -const {SlashCommandBuilder} = require("discord.js"); +const {SlashCommandBuilder, EmbedBuilder} = require("discord.js"); module.exports = { data: new SlashCommandBuilder() .setName("about") - .setDescription("Information about this bot"), + .setDescription("Information about this bot and creator"), async execute(interaction) { + const exampleEmbed = new EmbedBuilder() + .setColor(0x0099FF) + .setTitle('About Page') + .setDescription('I build this bot for managing my servers. (to lazy to write mor text rn)') + .setImage('https://i.postimg.cc/QdZZwKVD/Screenshot-2024-12-25-005933.png') + .setTimestamp(); + await interaction.reply({ - content: "This bot has some general commands for moderation, but includes on or two unqiue features, like the currenc system. It's a small project to enhanced the programming skills of the creator.", - ephemeral: true + embeds: [exampleEmbed] }) } } \ No newline at end of file diff --git a/db.js b/db.js new file mode 100644 index 0000000..d2a0598 --- /dev/null +++ b/db.js @@ -0,0 +1,30 @@ +const { MongoClient, ServerApiVersion } = require('mongodb'); +const { mongodb } = require("./config.json"); + +let dbClient; + +async function connectToDatabase() { + if (!dbClient) { + // Create a new MongoClient only once + dbClient = new MongoClient(mongodb, { + serverApi: { + version: ServerApiVersion.v1, + strict: true, + deprecationErrors: true, + } + }); + + await dbClient.connect(); + console.log("Connected to MongoDB!"); + + await dbClient.db("admin").command({ ping: 1 }); + console.log("Pinged your deployment. You successfully connected to MongoDB!"); + } + + // Return the existing connection if already connected + return dbClient; +} + +module.exports = { + connectToDatabase +}; diff --git a/events/ready.js b/events/ready.js index ea615aa..7115c92 100644 --- a/events/ready.js +++ b/events/ready.js @@ -1,9 +1,17 @@ const { Events } = require('discord.js'); +const { connectToDatabase } = require('../db.js'); module.exports = { name: Events.ClientReady, once: true, - execute(client) { + async execute(client) { console.log(`Ready! Logged in as ${client.user.tag}`); + + try { + await connectToDatabase(); + + } catch (err) { + console.error("MongoDB connection error:", err); + } }, }; diff --git a/package-lock.json b/package-lock.json index bc330ff..76fde88 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,6 +14,7 @@ "discord.js": "^14.17.3", "express": "^4.21.0", "moment": "^2.30.1", + "mongodb": "^6.12.0", "openai": "^4.67.3", "sqlite3": "^5.1.7" } @@ -152,6 +153,15 @@ "license": "MIT", "optional": true }, + "node_modules/@mongodb-js/saslprep": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.9.tgz", + "integrity": "sha512-tVkljjeEaAhCqTzajSdgbQ6gE6f3oneVwa3iXR6csiEwXXOFsiC6Uh9iAjAhXPtqa/XMDHWjjeNH/77m/Yq2dw==", + "license": "MIT", + "dependencies": { + "sparse-bitfield": "^3.0.3" + } + }, "node_modules/@npmcli/fs": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.1.tgz", @@ -240,6 +250,21 @@ "form-data": "^4.0.0" } }, + "node_modules/@types/webidl-conversions": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz", + "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==", + "license": "MIT" + }, + "node_modules/@types/whatwg-url": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-11.0.5.tgz", + "integrity": "sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==", + "license": "MIT", + "dependencies": { + "@types/webidl-conversions": "*" + } + }, "node_modules/@types/ws": { "version": "8.5.14", "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.14.tgz", @@ -505,6 +530,15 @@ "concat-map": "0.0.1" } }, + "node_modules/bson": { + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/bson/-/bson-6.10.2.tgz", + "integrity": "sha512-5afhLTjqDSA3akH56E+/2J6kTDuSIlBxyXPdQslj9hcIgOUE378xdOfZvC/9q3LifJNI6KR/juZ+d0NRNYBwXg==", + "license": "Apache-2.0", + "engines": { + "node": ">=16.20.1" + } + }, "node_modules/buffer": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", @@ -1541,6 +1575,12 @@ "node": ">= 0.6" } }, + "node_modules/memory-pager": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", + "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==", + "license": "MIT" + }, "node_modules/merge-descriptors": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", @@ -1748,6 +1788,96 @@ "node": "*" } }, + "node_modules/mongodb": { + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.12.0.tgz", + "integrity": "sha512-RM7AHlvYfS7jv7+BXund/kR64DryVI+cHbVAy9P61fnb1RcWZqOW1/Wj2YhqMCx+MuYhqTRGv7AwHBzmsCKBfA==", + "license": "Apache-2.0", + "dependencies": { + "@mongodb-js/saslprep": "^1.1.9", + "bson": "^6.10.1", + "mongodb-connection-string-url": "^3.0.0" + }, + "engines": { + "node": ">=16.20.1" + }, + "peerDependencies": { + "@aws-sdk/credential-providers": "^3.188.0", + "@mongodb-js/zstd": "^1.1.0 || ^2.0.0", + "gcp-metadata": "^5.2.0", + "kerberos": "^2.0.1", + "mongodb-client-encryption": ">=6.0.0 <7", + "snappy": "^7.2.2", + "socks": "^2.7.1" + }, + "peerDependenciesMeta": { + "@aws-sdk/credential-providers": { + "optional": true + }, + "@mongodb-js/zstd": { + "optional": true + }, + "gcp-metadata": { + "optional": true + }, + "kerberos": { + "optional": true + }, + "mongodb-client-encryption": { + "optional": true + }, + "snappy": { + "optional": true + }, + "socks": { + "optional": true + } + } + }, + "node_modules/mongodb-connection-string-url": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.2.tgz", + "integrity": "sha512-rMO7CGo/9BFwyZABcKAWL8UJwH/Kc2x0g72uhDWzG48URRax5TCIcJ7Rc3RZqffZzO/Gwff/jyKwCU9TN8gehA==", + "license": "Apache-2.0", + "dependencies": { + "@types/whatwg-url": "^11.0.2", + "whatwg-url": "^14.1.0 || ^13.0.0" + } + }, + "node_modules/mongodb-connection-string-url/node_modules/tr46": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.0.0.tgz", + "integrity": "sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==", + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/mongodb-connection-string-url/node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/mongodb-connection-string-url/node_modules/whatwg-url": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.1.0.tgz", + "integrity": "sha512-jlf/foYIKywAt3x/XWKZ/3rz8OSJPiWktjmk891alJUEjiVxKX9LEO92qH3hv4aJ0mN3MWPvGMCy8jQi95xK4w==", + "license": "MIT", + "dependencies": { + "tr46": "^5.0.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -2075,6 +2205,15 @@ "once": "^1.3.1" } }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/qs": { "version": "6.13.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", @@ -2424,6 +2563,15 @@ "node": ">= 10" } }, + "node_modules/sparse-bitfield": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", + "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==", + "license": "MIT", + "dependencies": { + "memory-pager": "^1.0.2" + } + }, "node_modules/sprintf-js": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", diff --git a/package.json b/package.json index 04bda05..afd5dba 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "discord.js": "^14.17.3", "express": "^4.21.0", "moment": "^2.30.1", + "mongodb": "^6.12.0", "openai": "^4.67.3", "sqlite3": "^5.1.7" }