From 0fb68532093005bff460f452ff380ef24f945f31 Mon Sep 17 00:00:00 2001 From: Robelo06 Date: Mon, 21 Oct 2024 17:17:43 +0200 Subject: [PATCH] main files --- Commands.md | 79 +++++ Privacy.md | 23 ++ README.md | 23 +- Terms.md | 21 ++ config.json | 14 + deploy-commands.js | 663 +++++++++++++++++++++++++++++++++++++ index.js | 128 +++++++ package.json | 19 ++ src/commands/actions.js | 135 ++++++++ src/commands/announce.js | 56 ++++ src/commands/avatar.js | 28 ++ src/commands/ban.js | 42 +++ src/commands/deafen.js | 33 ++ src/commands/disconnect.js | 33 ++ src/commands/embed.js | 90 +++++ src/commands/faq.js | 16 + src/commands/help.js | 26 ++ src/commands/info.js | 48 +++ src/commands/invite.js | 21 ++ src/commands/kick.js | 38 +++ src/commands/leave.js | 20 ++ src/commands/lock.js | 30 ++ src/commands/mcstatus.js | 104 ++++++ src/commands/meme.js | 10 + src/commands/modnick.js | 40 +++ src/commands/move.js | 35 ++ src/commands/ping.js | 15 + src/commands/poll.js | 92 +++++ src/commands/prune.js | 26 ++ src/commands/report.js | 66 ++++ src/commands/role.js | 25 ++ src/commands/say.js | 33 ++ src/commands/search.js | 48 +++ src/commands/server.js | 45 +++ src/commands/setnick.js | 44 +++ src/commands/suggestion.js | 61 ++++ src/commands/timeout.js | 48 +++ src/commands/timestamp.js | 41 +++ src/commands/unban.js | 25 ++ src/commands/undeafen.js | 33 ++ src/commands/unlock.js | 29 ++ src/commands/unmute.js | 33 ++ src/commands/user.js | 32 ++ src/commands/vmute.js | 33 ++ src/commands/vunmute.js | 33 ++ 45 files changed, 2529 insertions(+), 8 deletions(-) create mode 100644 Commands.md create mode 100644 Privacy.md create mode 100644 Terms.md create mode 100644 config.json create mode 100644 deploy-commands.js create mode 100644 index.js create mode 100644 package.json create mode 100644 src/commands/actions.js create mode 100644 src/commands/announce.js create mode 100644 src/commands/avatar.js create mode 100644 src/commands/ban.js create mode 100644 src/commands/deafen.js create mode 100644 src/commands/disconnect.js create mode 100644 src/commands/embed.js create mode 100644 src/commands/faq.js create mode 100644 src/commands/help.js create mode 100644 src/commands/info.js create mode 100644 src/commands/invite.js create mode 100644 src/commands/kick.js create mode 100644 src/commands/leave.js create mode 100644 src/commands/lock.js create mode 100644 src/commands/mcstatus.js create mode 100644 src/commands/meme.js create mode 100644 src/commands/modnick.js create mode 100644 src/commands/move.js create mode 100644 src/commands/ping.js create mode 100644 src/commands/poll.js create mode 100644 src/commands/prune.js create mode 100644 src/commands/report.js create mode 100644 src/commands/role.js create mode 100644 src/commands/say.js create mode 100644 src/commands/search.js create mode 100644 src/commands/server.js create mode 100644 src/commands/setnick.js create mode 100644 src/commands/suggestion.js create mode 100644 src/commands/timeout.js create mode 100644 src/commands/timestamp.js create mode 100644 src/commands/unban.js create mode 100644 src/commands/undeafen.js create mode 100644 src/commands/unlock.js create mode 100644 src/commands/unmute.js create mode 100644 src/commands/user.js create mode 100644 src/commands/vmute.js create mode 100644 src/commands/vunmute.js diff --git a/Commands.md b/Commands.md new file mode 100644 index 0000000..561a00b --- /dev/null +++ b/Commands.md @@ -0,0 +1,79 @@ +# Commands +Example: +`/command` | Command Description | Required Permissions, empty if none required + +-- + +- `/actions` | Moderate a Member with some actions | Set/ModNick: **ManageNicknames**, Timeout: **ModerateMembers**, Kick: **KickMembers**, Ban: **BanMembers** + +- `/announce` | Send an Announcement through the bot | **Administrator** + +- `/avatar` | Get the avatar of an User + +- `/ban` | Ban a Member | **BanMembers** + +- `/deafen` | Deafen a Member | **DeafenMembers** + +- `/disconnect` | Disconnect a Member from a Voice Channel | **MoveMembers** + +- `/embed` | Create and Post an embed | **Administrator** + +- `/faq` | USF Bot Frequently Asked Questions + +- `/help` | Get commands and info about the bot + +- `/info` | Get informations about the bot + +- `/invite` | Invite the bot to your servers + +- `/kick` | Kick a Member | **KickMembers** + +- `/leave` | Private Command for Bot Owners + +- `/lock` | Lock a Channel and post an embed with the reason | **ManageChannels** + +- `/mcstatus` | Get the status of a Minecraft Server in Java, Bedrock or Education Edition -- **Issues with Aternos and exaroton Servers on their end** + +- `/meme` | Generate a random Meme + +- `/modnick` | Moderate a Member nickname, format "Moderated Name XXXXXX" X = Number | **ManageNicknames** + +- `/move` | Move a Member to another Voice Channel | **MoveMembers** + +- `/ping` | Returns the Bot Ping + +- `/poll` | Create a poll in the server and max 10 choices | **Administrator** + +- `/prune` | Prune messages in a channel, up to 200 | **ManageMessages** + +- `/report` | Report an User or Issue/Bug with the bot to Developers + +- `/role` | Get Role Informations + +- `/say` | Say something in chat through the bot | **Administrator** + +- `/search` | Generate search links + +- `/server` | Display info about the server + +- `/setnick` | Set the Nickname of a Member | **ModerateNicknames** + +- `/suggestion` | Suggest something to be added in the bot to Developers + +- `/timeout` | Timeout a Server Member | **ModerateMembers** + +- `/timestamp` | Generates a Timestamp + +- `/unban` | unban an User from the Server | **BanMembers** + +- `/undeafen` | Undeafen a Member | **DeafenMembers** + +- `/unlock` | Unlock a channel and post an embed with the reason | **ManageChannels** + +- `/unmute` | Unmute a Member | **ModerateMembers** + +- `/user` | Get informations about an User + +- `/vmute` | Voice Mute a Member | **MuteMembers** + +- `/vunmute` | Voice Unmute a Member | **MuteMembers** diff --git a/Privacy.md b/Privacy.md new file mode 100644 index 0000000..6d73944 --- /dev/null +++ b/Privacy.md @@ -0,0 +1,23 @@ +# Privacy Policy +DISCLAIMER: We do not knowingly collect data of children under 16. Under article 8 of the GDPR, you are required to have your guardian(s) consent for you before using our products and services if you are under 16. + +## What data do we gather? +- Join and Leave logs for servers who invite the bot in +- Reports and suggestions sent to us + +## Why do we need this data? +We need this data to make sure that the USF Bot is not used for other scopes which are against the Discord ToS, Community guidelines or even law and to allow our users to send us suggestions and reports regarding the bot. + +## How can I request to get my data deleted? +- You can request to delete your server joinlogs if the bot left your discord server.... +- You can request to delete reports and suggestions sent to us... +...through a support Ticket in our Discord Server or mailing us to usfdiscordteam@gmail.com. + +## Who do we share your data with? +Only the USF Team has access to this data and they are not allowed to send it anywhere. + +## Where can we discuss about this Privacy Policy? +You can send a mail to usfdiscordteam@gmail.com or ask in our Discord Server (https://dsc.gg/usfteam) through a support ticket. + +## Changes in the privacy Policy +It is your responsibility to always stay informed of the latest Privacy Policy. Even if you haven't read it beforehand, you may still be subject to punishment. diff --git a/README.md b/README.md index 0cd781b..b7b6426 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,12 @@ -# USFBOT +# USF BOT The USF Bot is a Multipurpose Discord Bot created with the scope of helping every Servers with Moderation and Management, making some actions faster with Utility functions and Entertain the Community with Fun features! The Bot is 100% free and features we add are mostly suggested by our Community Members and Members in servers where the Bot is present. ## Informations -[Commands] Updating +[Commands](https://github.com/USF-Team/USFBOT/blob/main/Commands.md) -[Organization Website] Updating +[Organization Website](https://usfteam.pages.dev/) -[Status Page] Updating +[Status Page](https://usf.instatus.com/) ### Suggestions Update Coming Soon @@ -14,14 +14,21 @@ Update Coming Soon Update Coming Soon ## Contacts -[Discord Server](https://dsc.gg/ddateam) +[Discord Server](https://dsc.gg/usfteam) -[Email](mailto:dda.discordteam@gmail.com) +[Email](mailto:usfdiscordteam@gmail.com) [Organization Representative Email](mailto:robertbelotti06@gmail.com) ## Help & Support -[Discord](https://dsc.gg/ddateam) +[Discord](https://dsc.gg/usfteam) -[Email](mailto:dda.discordteam@gmail.com) +[Email](mailto:usfdiscordteam@gmail.com) + +## Legal +`License Type:` GNU GPL3 + +[Terms of Service](https://github.com/USF-Team/USFBOT/blob/main/Terms.md) + +[Privacy Policy](https://github.com/USF-Team/USFBOT/blob/main/Privacy.md) diff --git a/Terms.md b/Terms.md new file mode 100644 index 0000000..0f8995e --- /dev/null +++ b/Terms.md @@ -0,0 +1,21 @@ +# Terms of Service + +## What happens if I break the ToS? +We deserve the right to block you or/and your server from using our Bot and to report you to Discord or compentent authorities. We only allow you to appeal through the [support mail](mailto:usfdiscordteam@gmail.com) and if you didn't break the Terms of Service and we are not obliged to provide evidence for your punishment. + +## Limitation of Liability +We are not responsible for a wrong use of our bot and we deserve the right to remove access to any users/servers when we feel it appropriate. + +## You are not allowed to... +1. use the bot with other intents than what it was made for. +2. use the bot to break the Discord ToS / Guidelines or the Law +3. automate the usage of the bot. +4. use the bot in a way that can damage it. +5. abuse security flaws and/or bugs in the bot. +6. try to circumvent a block or edit permissions of the bot + +## What do I do if I find a bug or a security flaw? +Send a mail to usfdiscordteam@gmail.com or open a **Report** ticket in our [Discord server](https://dsc.gg/usfteam) + +## Changes in our Terms of Service +It is your responsibility to always stay informed of the latest Terms of Service. Even if you haven't read it beforehand, you may still be subject to punishment. \ No newline at end of file diff --git a/config.json b/config.json new file mode 100644 index 0000000..a10a94b --- /dev/null +++ b/config.json @@ -0,0 +1,14 @@ +{ + "token": "BOTTOKENHERE", + "clientId": "BOTID", + "guildId": "MAINGUILDID", + "version": "VERSION.", + "usf" : ["USFMEMBERSID"], + "bannedUsers": ["BANNEDUSERID"], + "bannedGuilds": ["BANNEDSERVERID"], + "discord": "DISCORDINVITE", + "joinCh": "BOTJOINCHANNEL", + "leaveCh": "BOTLEAVECHANNEL", + "reportCh": "BOTREPORTCHANNEL", + "suggestCh": "BOTSUGGESTCHANNEL" +} diff --git a/deploy-commands.js b/deploy-commands.js new file mode 100644 index 0000000..3218aca --- /dev/null +++ b/deploy-commands.js @@ -0,0 +1,663 @@ +const { REST, Routes } = require('discord.js'); +const { clientId, token, guildId } = require('./config.json'); +const fs = require('node:fs'); +const path = require('node:path'); +// +const commands = [ + { + "name": "actions", + "description": "Perform moderation actions on users", + "dm_permission": "false", + options: [ + { + "name": "target", + "description": "User to moderate", + "type": 6, + "required": "true" + }, + { + "name": "reason", + "description": "Reason of the moderation", + "type": 3, + "required": "false" + } + ] + }, + { + "name": "avatar", + "description": "Get the avatar of a selected user, or your own avatar", + "dm_permission": "true", + "integration_types": [0, 1], + "contexts": [0, 1, 2], + options : [ + { + "name": "user", + "description": "the user's avatar to show", + "type": 6, + "required": "false" + } + ] + }, + { + "name": "ban", + "description": "Ban an user", + "dm_permission": "false", + options: [ + { + "name": "target", + "description": "The user to ban", + "type": 6, + "required": "true" + }, + { + "name": "reason", + "description": "Reason of the ban", + "type": 3, + "required": "false" + } + ] + }, + { + "name": "embed", + "description": "Create an embed with text and settings decided by you", + "dm_permission": "false", + options: [ + { + "name": "color", + "description": "Select the embed color", + "type": 3, + "required": "false", + choices: [ + { + "name": "Pink", + "type": 3, + "value": "pink" + }, + { + "name": "Red", + "type": 3, + "value": "red" + }, + { + "name": "Orange", + "type": 3, + "value": "orange" + }, + { + "name": "Yellow", + "type": 3, + "value": "yellow" + }, + { + "name": "Green", + "type": 3, + "value": "green" + }, + { + "name": "Blue", + "type": 3, + "value": "blue" + }, + { + "name": "Purple", + "type": 3, + "value": "purple" + }, + { + "name": "White", + "type": 3, + "value": "white" + }, + { + "name": "Black", + "type": 3, + "value": "black" + } + ] + }, + { + "name": "footericon", + "description": "Should we add your avatar as footericon?", + "type": 5, + "required": "false" + }, + { + "name": "image", + "description": "Upload an image for the embed", + "type": 11, + "required": "false" + } + ] + }, + { + "name": "google", + "description": "Generate Google and LMGTFY Links", + "dm_permission": "true", + "integration_types": [0, 1], + "contexts": [0, 1, 2], + options: [ + { + "name": "text", + "description": "Google that for you", + "type": 3, + "required": "true" + } + ] + }, + { + "name": "help", + "description": "Get commands and info about the bot", + "dm_permission": "true", + "integration_types": [0, 1], + "contexts": [0, 1, 2] + }, + { + "name": "info", + "description": "Get informations about the bot", + "dm_permission": "true", + "integration_types": [0, 1], + "contexts": [0, 1, 2] + }, + { + "name": "kick", + "description": "Select and kick a member from the server", + "dm_permission": "false", + options: [ + { + "name": "target", + "description": "The member to kick", + "type": 6, + "required": "true" + }, + { + "name": "reason", + "description": "Kick reason", + "type": 3, + "required": "false" + } + ] + }, + { + "name": "lock", + "description": "Lock a channel and post an embed with the reason", + "dm_permission": "false", + options: [ + { + "name": "channel", + "description": "Channel to lock", + "type": 7, + "required": "false" + }, + { + "name": "reason", + "description": "Reason of the lock", + "type": 3, + "required": "false" + } + ] + }, + { + "name": "mcstatus", + "description": "Get the status of a Java, Bedrock or Education Edition Minecraft Server", + "dm_permission": "false", + options: [ + { + "name": "address", + "description": "IP address of the Minecraft server (without port)", + "type": 3, + "required": "true" + }, + { + "name": "edition", + "description": "The edition of the server", + "type": 3, + "required": "true", + choices: [ + { + "name": "Java Edition", + "type": 3, + "value": "java" + }, + { + "name": "Bedrock/Education Edition", + "type": 3, + "value": "bedrock" + } + ] + }, + { + "name": "port", + "description": "Port of the Minecraft Server (optional)", + "type": 3, + "required": "false", + } + ] + }, + { + "name": "meme", + "description": "Generate a random meme", + "dm_permission": "false" + }, + { + "name": "modnick", + "description": "Moderate an user nickname", + "dm_permission": "false", + options: [ + { + "name": "user", + "description": "User to moderate the nickname of", + "type": 6, + "required": "true" + }, + { + "name": "reason", + "description": "Moderation Reason", + "type": 3, + "required": "false" + }, + { + "name": "notify", + "description": "Should the bot notify the user via this chat?", + "type": 5, + "required": "false" + } + ] + }, + { + "name": "ping", + "description": "Return the ping of the bot", + "dm_permission": "true", + "integration_types": [0, 1], + "contexts": [0, 1, 2] + }, + { + "name": "poll", + "description": "Create a poll in the server, max 10 options", + "dm_permission": "false", + options: [ + { + "name": "message", + "description": "Poll Message", + "type": 3, + "required": "true" + }, + { + "name": "option1", + "description": "Option 1", + "type": 3, + "required": "true" + }, + { + "name": "option2", + "description": "Option 2", + "type": 3, + "required": "true" + }, + { + "name": "option3", + "description": "Option 3", + "type": 3, + "required": "false" + }, + { + "name": "option4", + "description": "Option 4", + "type": 3, + "required": "false" + }, + { + "name": "option5", + "description": "Option 5", + "type": 3, + "required": "false" + }, + { + "name": "option6", + "description": "Option 6", + "type": 3, + "required": "false" + }, + { + "name": "option7", + "description": "Option 7", + "type": 3, + "required": "false" + }, + { + "name": "option8", + "description": "Option 8", + "type": 3, + "required": "false" + }, + { + "name": "option9", + "description": "Option 9", + "type": 3, + "required": "false" + }, + { + "name": "option10", + "description": "Option 10", + "type": 3, + "required": "false" + } + ] + }, + { + "name": "prune", + "description": "Prune messages in a channel, up to 200", + "dm_permission": "false", + options: [ + { + "name": "amount", + "description": "Amount of messages to prune", + "type": 4, + "required": "true" + } + ] + }, + { + "name": "report", + "description": "Report an user or something wrong with the bot", + "dm_permission": "true", + "integration_types": [0, 1], + "contexts": [0, 1, 2], + options: [ + { + "name": "reported", + "description": "What are you reporting? choose an option", + "type": 3, + "required": "true", + choices: [ + { + "name": "An User", + "type": 3, + "value": "user" + }, + { + "name": "Bot Function", + "type": 3, + "value": "bot" + } + ] + }, + { + "name": "proof", + "description": "Send us proof about your report", + "type": 11, + "required": "false" + }, + { + "name": "other-info", + "description": "Other informations about your report", + "type": 3, + "required": "false" + } + ] + }, + { + "name": "say", + "description": "Say something in chat through the bot", + "dm_permission": "false" + }, + { + "name": "server", + "description": "Display info about this server", + "dm_permission": "false" + }, + { + "name": "setnick", + "description": "Set the nickname of an user", + "dm_permission": "false", + options: [ + { + "name": "target", + "description": "User to change the name of", + "type": 6, + "required": "true" + }, + { + "name": "new-nickname", + "description": "The nickname to set to the user", + "type": 3, + "required": "true" + }, + { + "name": "reason", + "description": "Moderation Reason", + "type": 3, + "required": "false" + }, + { + "name": "notify", + "description": "Should the bot notify the user via this chat?", + "type": 5, + "required": "false" + } + ] + }, + { + "name": "suggestion", + "description": "Suggest us something for the USF Bot", + "dm_permission": "true", + "integration_types": [0, 1], + "contexts": [0, 1, 2], + options: [ + { + "name": "type", + "description": "What you want to suggest", + "type": 3, + "required": "true", + choices: [ + { + "name": "Command", + "type": 3, + "value": "command" + }, + { + "name": "Option", + "type": 3, + "value": "option" + }, + { + "name": "Command Changes", + "type": 3, + "value": "change" + }, + { + "name": "Function", + "type": 3, + "value": "function" + } + ] + }, + { + "name": "description", + "description": "Describe in detail what you want to add/change", + "type": 3, + "required": "true" + } + ] + }, + { + "name": "timeout", + "description": "Timeout a guild member", + "dm_permission": "false", + options: [ + { + "name": "target", + "description": "Member to timeout", + "type": 6, + "required": "true" + }, + { + "name": "duration", + "description": "Duration of the timeout", + "type": 3, + "required": "true" + }, + { + "name": "reason", + "description": "Reason of the timeout", + "type": 3, + "required": "false" + } + ] + }, + { + "name": "timestamp", + "description": "Generate your timestamp", + "dm_permission": "true", + "integration_types": [0, 1], + "contexts": [0, 1, 2], + options : [ + { + "name": "type", + "description": "Type of the timestamp", + "type": 3, + "required": "true", + choices : [ + {"name": 'Short Time', "type": 3, "value": '1'}, + {"name": 'Long Time', "type": 3, "value": '2'}, + {"name": 'Short Date', "type": 3, "value": '3'}, + {"name": 'Long Date', "type": 3, "value": '4'}, + {"name": 'Long Date with Short Time', "type": 3, value: '5'}, + {"name": 'Long Date with day of the week and short time', "type": 3, "value": '6'}, + {"name": 'Relative', "type": 3, "value": '7'} + ] + }, + { + "name": "year", + "description": "Year of the timestamp", + "type": 4, + "required": "true" + }, + { + "name": "month", + "description": "Month of the timestamp", + "type": 4, + "required": "true" + }, + { + "name": "day", + "description": "Day of the timestamp", + "type": 4, + "required": "true" + }, + { + "name": "hour", + "description": "Hour of the timestamp", + "type": 4, + "required": "false" + }, + { + "name": "minute", + "description": "Minute of the timestamp", + "type": 4, + "required": "false" + }, + ] + }, + { + "name": "unban", + "description": "Unban an user from the server", + "dm_permission": "false", + options: [ + { + "name": "target", + "description": "Target user of the moderation", + "type": 6, + "required": "true" + }, + { + "name": "reason", + "description": "Reason of the moderation", + "type": 3, + "required": "false" + } + ] + }, + { + "name": "unlock", + "description": "Unlock a channel and post an embed with the reason", + "dm_permission": "false", + options: [ + { + "name": "channel", + "description": "Channel to unlock", + "type": 7, + "required": "false" + }, + { + "name": "reason", + "description": "Reason of the unlock", + "type": 3, + "required": "false" + } + ] + }, + { + "name": "user", + "description": "Get informations about an user", + "dm_permission": "true", + "integration_types": [0, 1], + "contexts": [0, 1, 2], + options: [ + { + "name": "target", + "description": "User you want to view", + "type": 6, + "required": "false" + } + ] + }, +]; +console.log(commands); +const usfcmds = [ + { + "name": "leave", + "description": "Leaves a Guild", + "dm_permission": "false", + options: [ + { + "name": "guildid", + "description": "ID of the Guild", + "type": 3, + "required": "true" + } + ] + } +]; +const commandsPath = path.join(__dirname, 'src'); +const commandFiles = fs.readdirSync(commandsPath).filter(file => file.endsWith('.js')); +// +for (const file of commandFiles) { + const command = require(`./src/commands/${file}`); + commands.push(command.data.toJSON()); +} +const rest = new REST({ version: '10' }).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); + } +})(); +(async () => { + try { + console.log(`Started refreshing ${usfcmds.length} guild application (/) commands.`); + const data2 = await rest.put( + Routes.applicationGuildCommands(clientId, guildId), + { body: usfcmds }, + ); + + console.log(`Successfully reloaded ${data2.length} guild application (/) commands.`); + } catch (error) { + console.error(error); + } +})(); \ No newline at end of file diff --git a/index.js b/index.js new file mode 100644 index 0000000..0e0dfd2 --- /dev/null +++ b/index.js @@ -0,0 +1,128 @@ +const fs = require('node:fs'); +const path = require('node:path'); +const { ActivityType, Client, Collection, EmbedBuilder, Events, GatewayIntentBits } = require('discord.js'); +const { token, bannedUsers, bannedGuilds, discord, joinCh, leaveCh } = require('./config.json'); +const client = new Client({ intents: [GatewayIntentBits.Guilds, GatewayIntentBits.DirectMessages], presence: {status: 'ONLINE', activities: [{type: ActivityType.Playing, name: '/info'}]} }); +// +client.cooldowns = new Collection(); +client.commands = new Collection(); +const foldersPath = path.join(__dirname, 'src'); +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.`); + } + } +} +// +client.once(Events.ClientReady, () => { + console.log(`Ready! Logged in as ${client.user.tag}!`); +}); +client.on(Events.InteractionCreate, async interaction => { + if (bannedUsers.includes(interaction.user.id)) { + const userNotAllowed = new EmbedBuilder() + .setTitle('You are not allowed to use this Bot!') + .setDescription(`Hello ${interaction.user},\nWe are sorry but you are not allowed to use the USF Bot.\n\nThis usually happens when you break our [Terms of Service](https://github.com/USF-Team/USFBOT#terms-of-service)\nIf you believe this is an error, you can appeal in our [Discord Server](${discord}).`); + return interaction.reply({embeds: [userNotAllowed], ephemeral: true}); + } + if (interaction.guild) { + if (bannedGuilds.includes(interaction.guild.id)) { + const guildNotAllowed = new EmbedBuilder() + .setTitle('This guild is not allowed to use this Bot!') + .setDescription(`Hello ${interaction.user},\nWe are sorry but this guild is not allowed to use the USF Bot.\n\nThis usually happens when guild members break our [Terms of Service](https://github.com/USF-Team/USFBOT#terms-of-service)\nIf you believe this is an error and you are the guild owner, you can appeal in our [Discord Server](${discord}).`); + return interaction.reply({embeds: [guildNotAllowed], ephemeral: true}); + } + } + // + if (!interaction.isChatInputCommand()) return; + const command = client.commands.get(interaction.commandName); + if (!command) return; + // + const { cooldowns } = client; + if (!cooldowns.has(command.name)) { + cooldowns.set(command.name, new Collection()); + } + const now = Date.now(); + const timestamps = cooldowns.get(command.name); + const defaultCooldownDuration = 3; + const cooldownAmount = (command.cooldown ?? defaultCooldownDuration) * 1000; + // + if (timestamps.has(interaction.user.id)) { + const expirationTime = timestamps.get(interaction.user.id) + cooldownAmount; + if (now more second(s) before reusing the \`${command.name}\` command.`, ephemeral: true }); + return; + } + } + // + timestamps.set(interaction.user.id, now); + setTimeout(() => timestamps.delete(interaction.user.id), cooldownAmount); + try { + await command.execute(interaction); + } catch (error) { + console.error(error); + const erbed = new EmbedBuilder() + .setColor(0xff0000) + .setTitle('We\'re sorry, an error occurred!') + .setDescription(`Please wait a few seconds and if the error persists, please contact the Development Team in our [Discord Server](${discord})`); + if (interaction.replied) { + await interaction.editReply({ embeds: [erbed], ephemeral: true }); + } else { + await interaction.reply({ embeds: [erbed], ephemeral: true }); + } + } +}); +client.on(Events.GuildCreate, guild => { + const joinch = client.channels.cache.get(joinCh); + const joinembed = new EmbedBuilder() + .setColor(0x00ff00) + .setTitle('Joined a new Guild') + .addFields( + { name: 'Server name', value: `${guild.name}` }, + { name: '\u200B', value: '\u200B' }, + { name: 'Members', value: `${guild.memberCount}`, inline: true }, + { name: 'ID', value: `${guild.id}`, inline: true }, + { name: 'GuildsCount', value: `${client.guilds.cache.size}`, inline: true }, + ) + .setTimestamp(); + if (guild.icon) { + joinembed.setThumbnail(`${guild.iconURL({ size: 2048 }) }`); + } + joinch.send({embeds:[joinembed]}); + if (bannedGuilds.includes(guild.id)) { + guild.leave(); + joinch.send('This Guild is blacklisted. The Bot left the guild.'); + } +}); +client.on(Events.GuildDelete, async guild => { + if (client.isReady()) { + const leavech = await client.channels.fetch(leaveCh); + const leaveEmbed = new EmbedBuilder() + .setColor(0xff0000) + .setTitle('Left a Guild') + .addFields( + { name: 'Server Name', value: `${guild.name}`}, + { name: '\u200B', value: '\u200B' }, + { name: 'Members', value: `${guild.memberCount}`, inline: true }, + { name: 'ID', value: `${guild.id}`, inline: true }, + { name: 'GuildsCount', value: `${client.guilds.cache.size}`, inline: true }, + ) + .setTimestamp(); + if (guild.icon) { + leaveEmbed.setThumbnail(`${guild.iconURL({ size: 2048 }) }`); + } + leavech.send({ embeds: [leaveEmbed] }); + } +}); +process.on('unhandledRejection', console.error) +process.on('uncaughtException', console.error) +client.login(token); \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..e0d3257 --- /dev/null +++ b/package.json @@ -0,0 +1,19 @@ +{ + "name": "nodejs", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "@types/node": "^18.0.6", + "discord.js": "^14.13.0", + "mc-server-status": "^2.0.2", + "ms": "^2.1.3", + "node-mcstatus": "^1.1.0" + } +} diff --git a/src/commands/actions.js b/src/commands/actions.js new file mode 100644 index 0000000..c27da4f --- /dev/null +++ b/src/commands/actions.js @@ -0,0 +1,135 @@ +const { EmbedBuilder } = require('@discordjs/builders'); +const { ActionRowBuilder, ButtonBuilder, ButtonStyle, ComponentType, ModalBuilder, PermissionsBitField, SlashCommandBuilder, TextInputBuilder, TextInputStyle } = require('discord.js'); +var ms = require('ms'); +// +module.exports = { + data: new SlashCommandBuilder() + .setName('actions').setDescription('Perform moderation actions on users') + .addUserOption(option=>option.setName('target').setDescription('User to moderate').setRequired(true)) + .addStringOption(option=>option.setName('reason').setDescription('Reason of the moderation')) + .setDMPermission(false), + async execute(interaction) { + await interaction.deferReply({ephemeral: true}) + const target = interaction.options.getMember('target'); + const reason = interaction.options.getString('reason') ?? 'No Reason Provided'; + // + const manage = new EmbedBuilder() + .setColor(0xff0000) + .setTitle(`Actions for ${target.user.username}`) + .setThumbnail(`${target.displayAvatarURL({size:2048})}`) + .setDescription(`**User Tag:** ${target} - **User ID:** ${target.id}\n**Selected Reason:** ${reason}`) + .setFooter({text: 'Please select a punishment!'}); + // + const setnick = new ButtonBuilder() + .setCustomId('setnick') + .setLabel('Change Nickname') + .setStyle(ButtonStyle.Secondary) + .setEmoji('🛡'); + const mute = new ButtonBuilder() + .setCustomId('mute') + .setLabel('Mute') + .setStyle(ButtonStyle.Secondary) + .setEmoji('🔇'); + const kick = new ButtonBuilder() + .setCustomId('kick') + .setLabel('Kick') + .setStyle(ButtonStyle.Secondary) + .setEmoji('🦶'); + const ban = new ButtonBuilder() + .setCustomId('ban') + .setLabel('Ban') + .setStyle(ButtonStyle.Danger) + .setEmoji('🔨'); + // + const actions = new ActionRowBuilder().addComponents(setnick, mute, kick, ban); + const response = await interaction.editReply({ embeds: [manage], components: [actions], ephemeral: true }); + try { + const collector = await response.createMessageComponentCollector({ componentType: ComponentType.Button, time: 60000 }); + collector.on('collect', async i => { + if (i.customId==='setnick') { + if (!(interaction.member.permissions.has(PermissionsBitField.Flags.ManageNicknames))) { + return i.reply({content: 'Unauthorized to execute this action!', ephemeral: true}); + } + if (target.permissions.has(PermissionsBitField.Flags.ManageNicknames)) { + return i.reply({content: 'Unauthorized to execute this action!', ephemeral: true}); + } + const nickModal = new ModalBuilder() + .setCustomId('respModal') + .setTitle('Change Nickname'); + const newnick = new TextInputBuilder() + .setCustomId('newnicknameinput') + .setLabel('New Nickname for the target member') + .setStyle(TextInputStyle.Short) + .setMaxLength(42) + .setRequired(true); + const newnickrow = new ActionRowBuilder().addComponents(newnick); + nickModal.addComponents(newnickrow); + i.showModal(nickModal); + i.awaitModalSubmit({time: 1000000}) + .then(interaction => { + const nn = interaction.fields.getTextInputValue('newnicknameinput'); + target.setNickname(nn, `[${interaction.user.username}] : ${reason}`) + .then(interaction.reply({content: 'Action Executed Successfully!', ephemeral: true})) + .catch(error => interaction.reply({content: `An error occurred while performing this action:\n${error}`, ephemeral: true})); + }).catch(error => { + console.error(error); + i.reply({content: 'Time ended', ephemeral: true}); + }); + } else if (i.customId==='mute') { + if (!(interaction.member.permissions.has(PermissionsBitField.Flags.ModerateMembers))) { + return i.reply({content: 'Unauthorized to execute this action!', ephemeral: true}); + } + if (target.permissions.has(PermissionsBitField.Flags.ModerateMembers)) { + return i.reply({content: 'Unauthorized to execute this action!', ephemeral: true}); + } + const timeModal = new ModalBuilder() + .setCustomId('respModal') + .setTitle('Change Nickname'); + const timedur = new TextInputBuilder() + .setCustomId('durationinput') + .setLabel('Timeout Duration') + .setStyle(TextInputStyle.Short) + .setMinLength(2) + .setMaxLength(10) + .setRequired(true); + const timedurrow = new ActionRowBuilder().addComponents(timedur); + timeModal.addComponents(timedurrow); + i.showModal(timeModal); + i.awaitModalSubmit({time: 1000000}) + .then(interaction => { + const tr = interaction.fields.getTextInputValue('durationinput'); + const time = ms(tr); + target.timeout(time, `[${interaction.user.username}] : ${reason}`) + .then(interaction.reply({content: 'Action Executed Successfully!', ephemeral: true})) + .catch(error => interaction.reply({content: `An error occurred while performing this action:\n${error}`, ephemeral: true})); + }).catch(error => { + console.log(error); + i.reply({content: 'Time ended', ephemeral: true}); + }) + } else if (i.customId==='kick') { + if (!(interaction.member.permissions.has(PermissionsBitField.Flags.KickMembers))) { + return i.reply({content: 'Unauthorized to execute this action!', ephemeral: true}); + } + if (target.permissions.has(PermissionsBitField.Flags.KickMembers)) { + return i.reply({content: 'Unauthorized to execute this action!', ephemeral: true}); + } + target.kick(`[${interaction.user.username}] : ${reason}`) + .then(i.reply({ content: `${target} has been kicked for reason: ${reason}`, components: [], ephemeral: true })) + .catch(error => i.reply({content: `An error occurred while performing this action:\n${error}`, ephemeral: true})); + } else if (i.customId==='ban') { + if (!(interaction.member.permissions.has(PermissionsBitField.Flags.BanMembers))) { + return i.reply({content: 'Unauthorized to execute this action!', ephemeral: true}); + } + if (target.permissions.has(PermissionsBitField.Flags.BanMembers)) { + return i.reply({content: 'Unauthorized to execute this action!', ephemeral: true}); + } + target.ban({user: target, reason: `[${interaction.user.username}] : ${reason}`}) + .then(i.reply({ content: `${target} has been banned for reason: ${reason}`, components: [], ephemeral: true })) + .catch(error=> interaction.followUp({content: `An error occurred while performing this action:\n${error}`, ephemeral: true})); + } + }) + } catch (error) { + console.error(error) + } + } +} \ No newline at end of file diff --git a/src/commands/announce.js b/src/commands/announce.js new file mode 100644 index 0000000..ae8bcd4 --- /dev/null +++ b/src/commands/announce.js @@ -0,0 +1,56 @@ +const { ModalBuilder, SlashCommandBuilder, TextInputBuilder, TextInputStyle, ActionRowBuilder, PermissionsBitField } = require('discord.js') +// +module.exports = { + data: new SlashCommandBuilder() + .setName('announce').setDescription('Send an Announcement through the bot') + .addChannelOption(option=>option.setName('channel').setDescription('Announcement Channel')) + .addMentionableOption(option=>option.setName('mention').setDescription('Role/Member to mention')) + .setDMPermission(false), + async execute(interaction) { + if (!(interaction.member.permissions.has(PermissionsBitField.Flags.Administrator))) { + return interaction.reply({ content: 'Unauthorized to execute the action! Only Administrators are allowed', ephemeral: true }) + } + const channel = interaction.options.getChannel('channel') ?? interaction.channel; + const mention = interaction.options.getMentionable('mention'); + const announcement = new ModalBuilder() + .setCustomId('announced') + .setTitle('Announcement Text'); + const title = new TextInputBuilder() + .setCustomId('title') + .setLabel('Announcement Title') + .setMaxLength(100) + .setPlaceholder('Uses markdown, header h1') + .setRequired(false) + .setStyle(TextInputStyle.Short); + const description = new TextInputBuilder() + .setCustomId('description') + .setLabel('Announcement Description') + .setMaxLength(1900) + .setPlaceholder('You can use markdown features, use the tag for external emojis') + .setRequired(true) + .setStyle(TextInputStyle.Paragraph); + const tit = new ActionRowBuilder().addComponents(title) + const desc = new ActionRowBuilder().addComponents(description) + announcement.addComponents(tit, desc); + await interaction.showModal(announcement) + const filter = (interaction) => interaction.customId === 'announced'; + interaction.awaitModalSubmit({filter, time: 3600000}) + .then(interaction=> { + interaction.reply('Announcement Text submitted successfully!') + const TextTitle = interaction.fields.getTextInputValue('title') + const TextDescription = interaction.fields.getTextInputValue('description') + if (mention && TextTitle) { + channel.send(`${mention}\n# ${TextTitle}\n${TextDescription}`); + } else if (mention && !TextTitle) { + channel.send(`${mention}\n${TextDescription}`) + } else if (!mention && TextTitle) { + channel.send(`# ${TextTitle}\n${TextDescription}`) + } else { + channel.send(`${TextDescription}`) + } + return; + }).catch(error => { + return interaction.reply(`There was an error while trying to get or send the announcement text!\n${error}`); + }) + } +} \ No newline at end of file diff --git a/src/commands/avatar.js b/src/commands/avatar.js new file mode 100644 index 0000000..7c23c1f --- /dev/null +++ b/src/commands/avatar.js @@ -0,0 +1,28 @@ +const { EmbedBuilder, SlashCommandBuilder } = require('discord.js'); +const { discord } = require('../../config.json'); +// +module.exports = { + data: new SlashCommandBuilder() + .setName('avatar').setDescription('Get the avatar of a selected user, or your own avatar') + .addUserOption(option=>option.setName('user').setDescription('The user\'s avatar to show')) + .setDMPermission(true), + async execute(interaction) { + await interaction.deferReply(); + try { + const user = interaction.options.getUser('user') ?? interaction.user; + const userEmbed = new EmbedBuilder() + .setColor(0x000fff) + .setTitle(`${user.username}\'s avatar`) + .setImage(`${user.displayAvatarURL({ dynamic: true, size: 4096 })}`) + .setTimestamp(); + return interaction.editReply({ embeds: [userEmbed] }); + } catch (error) { + console.error(error); + const erbed = new Embedbuilder() + .setColor(0xff0000) + .setTitle('We\'re sorry, an error occurred!') + .setDescription(`Please wait a few seconds and if the error persists, please contact the Development Team in our [Discord Server](${discord})\nError: ${error}`); + return interaction.editReply({ embeds: [erbed] }); + } + } +} \ No newline at end of file diff --git a/src/commands/ban.js b/src/commands/ban.js new file mode 100644 index 0000000..28ce321 --- /dev/null +++ b/src/commands/ban.js @@ -0,0 +1,42 @@ +const { PermissionsBitField, SlashCommandBuilder, EmbedBuilder } = require('discord.js'); +// +module.exports = { + data: new SlashCommandBuilder() + .setName('ban').setDescription('Ban a Member') + .addUserOption(option=>option.setName('target').setDescription('Member to Ban').setRequired(true)) + .addStringOption(option=>option.setName('reason').setDescription('Ban Reason')) + .setDMPermission(false), + async execute(interaction) { + await interaction.deferReply({ ephemeral: true }) + const target = interaction.options.getMember('target') + const reason = interaction.options.getString('reason') ?? 'No reason provided' + let banEmbed = new EmbedBuilder(); + if (!(interaction.member.permissions.has(PermissionsBitField.Flags.BanMembers))) { + return interaction.editReply(`You are not allowed to perform this action!\n **Reason:** Missing \`BanMembers\` Permission`) + } + if (!(target.bannable)) { + return interaction.editReply(`The Bot is not allowed to Moderate this Member!`) + } + if (interaction.member.roles.highest.position < target.roles.highest.position) { + if (!(interaction.member.id === interaction.guild.ownerId)) { + return interaction.editReply(`You are not allowed to perform this action!\n **Reason:** Member with Higher rank`) + } + } + target.send(`You have been **banned** from \`${interaction.guild.name}\` | ${reason}`) + .then(i => { + banEmbed.setFooter({ text: 'User DMed successfully!'}); + }) + .catch(error => { + console.error(error) + banEmbed.setFooter({ text: 'Could not DM the User' }); + }); + target.ban({ reason: `[${interaction.user.username}] : ${reason}` }) + .then(i => { + banEmbed.setColor(0x00ff00).setDescription(`${target} has been **banned** | ${reason}`) + return interaction.editReply({ embeds: [banEmbed] }) + }).catch(error => { + console.error(error) + return interaction.editReply({ content: `An Error Occurred!\n${error}` }) + }); + } +} \ No newline at end of file diff --git a/src/commands/deafen.js b/src/commands/deafen.js new file mode 100644 index 0000000..84efc22 --- /dev/null +++ b/src/commands/deafen.js @@ -0,0 +1,33 @@ +const { EmbedBuilder, SlashCommandBuilder, PermissionsBitField } = require('discord.js') +// +module.exports = { + data: new SlashCommandBuilder() + .setName('deafen').setDescription('Deafen a Member') + .addUserOption(option=>option.setName('target').setDescription('Member to deafen').setRequired(true)) + .addStringOption(option=>option.setName('reason').setDescription('Deafen reason').setMaxLength(200)) + .setDMPermission(false), + async execute(interaction) { + await interaction.deferReply({ ephemeral: true }) + const target = interaction.options.getMember('target') + const reason = interaction.options.getString('reason') ?? `No Reason Provided` + let deafened = new EmbedBuilder(); + if (interaction.member.permissions.has(PermissionsBitField.Flags.DeafenMembers)) { + try { + if (!(target.voice.channel)) { + deafened.setColor(0xff0000).setDescription(`The Member is not in a Voice Channel!`); + return interaction.editReply({ embeds: [deafened] }) + } + target.edit({ deaf: true, reason: `[${interaction.user.username}]: ${reason}` }) + deafened.setColor(0x00ff00).setDescription('Member deafened successfully!'); + return interaction.editReply({ embeds: [deafened] }) + } catch (error) { + console.error(error) + deafened.setColor(0xff0000).setDescription(`An unknown error occurred while executing this command!\n${error}`); + return interaction.editReply({ embeds: [deafened] }) + } + } else { + deafened.setColor(0xff0000).setDescription('You are missing the required permission to run this command: `DeafenMembers`'); + return interaction.editReply({ embeds: [deafened] }) + } + } +} \ No newline at end of file diff --git a/src/commands/disconnect.js b/src/commands/disconnect.js new file mode 100644 index 0000000..4b775fd --- /dev/null +++ b/src/commands/disconnect.js @@ -0,0 +1,33 @@ +const { EmbedBuilder, SlashCommandBuilder, PermissionsBitField } = require('discord.js') +// +module.exports = { + data: new SlashCommandBuilder() + .setName('disconnect').setDescription('Disconnect a Member from a Voice Channel') + .addUserOption(option=>option.setName('target').setDescription('Member to disconnect').setRequired(true)) + .addStringOption(option=>option.setName('reason').setDescription('Disconnect reason').setMaxLength(200)) + .setDMPermission(false), + async execute(interaction) { + await interaction.deferReply({ ephemeral: true }) + const target = interaction.options.getMember('target') + const reason = interaction.options.getString('reason') ?? `No Reason Provided` + let disconnected = new EmbedBuilder(); + if (interaction.member.permissions.has(PermissionsBitField.Flags.MoveMembers)) { + try { + if (!(target.voice.channel)) { + disconnected.setColor(0xff0000).setDescription(`The Member is not in a Voice Channel!`); + await interaction.editReply({ embeds: [disconnected] }); return; + } + target.voice.disconnect(null, `[${interaction.user.username}]: ${reason}`) + disconnected.setColor(0x00ff00).setDescription('Member disconnected successfully!'); + await interaction.editReply({ embeds: [disconnected] }); return; + } catch (error) { + console.error(error) + disconnected.setColor(0xff0000).setDescription(`An unknown error occurred while executing this command!\n${error}`); + await interaction.editReply({ embeds: [disconnected] }); return; + } + } else { + disconnected.setColor(0xff0000).setDescription('You are missing the required permission to run this command: `ModerateMembers`'); + await interaction.editReply({ embeds: [disconnected] }); return; + } + } +} \ No newline at end of file diff --git a/src/commands/embed.js b/src/commands/embed.js new file mode 100644 index 0000000..04771f8 --- /dev/null +++ b/src/commands/embed.js @@ -0,0 +1,90 @@ +const { ActionRowBuilder, EmbedBuilder, ModalBuilder, PermissionsBitField, SlashCommandBuilder, TextInputBuilder, TextInputStyle } = require('discord.js'); +// +module.exports = { + data: new SlashCommandBuilder() + .setName('embed').setDescription('Creates an embed') + .addStringOption(option=>option.setName('color').setDescription('Select the color of the embed').addChoices( + {name: 'Pink', value: 'pink'}, + {name: 'Red', value: 'red'}, + {name: 'Orange', value: 'orange'}, + {name: 'Yellow', value: 'yellow'}, + {name: 'Green', value: 'green'}, + {name: 'Blue', value: 'blue'}, + {name: 'Purple', value: 'purple'}, + {name: 'Black', value: 'black'}, + {name: 'White', value: 'white'}, + )) + .addBooleanOption(option=>option.setName('footericon').setDescription('Should we add your avatar as footericon?')) + .addAttachmentOption(option=>option.setName('image').setDescription('Upload an image for the embed')) + .setDMPermission(false), + async execute(interaction) { + if (interaction.member.permissions.has(PermissionsBitField.Flags.Administrator)) { + let color = interaction.options.getString('color') ?? 'black'; + const footericon = interaction.options.getBoolean('footericon'); + const image = interaction.options.getAttachment('image'); + const modal = new ModalBuilder() + .setCustomId('embedResponse') + .setTitle('Embed Options'); + const titleb = new TextInputBuilder() + .setCustomId('title') + .setLabel('Embed Title') + .setStyle(TextInputStyle.Short) + .setRequired(true); + const descriptionb = new TextInputBuilder() + .setCustomId('description') + .setLabel('Embed Description') + .setStyle(TextInputStyle.Paragraph) + .setRequired(true); + const footerb = new TextInputBuilder() + .setCustomId('footer') + .setLabel('Embed Footer') + .setStyle(TextInputStyle.Short) + .setRequired(false); + const row = new ActionRowBuilder().addComponents(titleb); + const row1 = new ActionRowBuilder().addComponents(descriptionb); + const row2 = new ActionRowBuilder().addComponents(footerb); + modal.addComponents(row, row1, row2); + await interaction.showModal(modal); + const filter = (interaction) => interaction.customId === 'embedResponse'; + interaction.awaitModalSubmit({ filter, time: 120000 }) + .then( async interaction => { + const title = interaction.fields.getTextInputValue('title'); + const description = interaction.fields.getTextInputValue('description'); + const footer = interaction.fields.getTextInputValue('footer'); + const embed = new EmbedBuilder() + .setTitle(`${title}`) + .setDescription(`${description}`) + .setTimestamp(); + switch (color) { + case 'blue': embed.setColor(0x0000ff); break; + case 'red': embed.setColor(0xff0000); break; + case 'orange': embed.setColor(0xff7700); break; + case 'green': embed.setColor(0x00ff00); break; + case 'yellow': embed.setColor(0xffff00); break; + case 'purple': embed.setColor(0xff00ff); break; + case 'black': embed.setColor(0x000001); break; + case 'white': embed.setColor(0xffffff); break; + case 'pink': embed.setColor(0xf887df); break; + default: embed.setColor(0x000001); + } + if (footer&&footericon) { + await embed.setFooter({text:`${footer}`, iconURL:`${interaction.user.displayAvatarURL({size:16})}`}); + } else if (footer&&!footericon) { + await embed.setFooter({text: `${footer}`}); + } else if (!footer&&footericon) { + await embed.setFooter({text: 'No Text Provided', iconURL: `${interaction.user.displayAvatarURL({size:16})}`}); + } + if (image) { + embed.setImage(`attachment://${image.name}`); + interaction.channel.send({embeds: [embed], files: [image] }); + return interaction.reply({content: 'Embed Sent', ephemeral: true}); + } + interaction.channel.send({embeds: [embed] }); + return interaction.reply({content: 'Embed Sent', ephemeral: true}); + }) + .catch(error =>{ + console.error(error); + }); + } + }, +}; \ No newline at end of file diff --git a/src/commands/faq.js b/src/commands/faq.js new file mode 100644 index 0000000..b9be5a9 --- /dev/null +++ b/src/commands/faq.js @@ -0,0 +1,16 @@ +const { SlashCommandBuilder, EmbedBuilder } = require('discord.js') +// +module.exports = { + data: new SlashCommandBuilder() + .setName('faq').setDescription('USF Bot Frequently Asked Questions') + .setDMPermission(true), + async execute(interaction) { + await interaction.deferReply() + const faqs = new EmbedBuilder() + .setColor(0x0000ff) + .setTitle('USFBot FAQs') + .setDescription('Currently, there are no FAQs Available! Feel free to ask any question in our Discord Support Server') + .setTimestamp(); + return interaction.editReply({ embeds: [faqs] }) + } +} \ No newline at end of file diff --git a/src/commands/help.js b/src/commands/help.js new file mode 100644 index 0000000..e44f3f5 --- /dev/null +++ b/src/commands/help.js @@ -0,0 +1,26 @@ +const { ActionRowBuilder, ButtonBuilder, ButtonStyle, EmbedBuilder, SlashCommandBuilder } = require('discord.js'); +const { discord } = require('../../config.json'); +// +module.exports = { + data: new SlashCommandBuilder() + .setName('help').setDescription('Get commands and info about the bot').setDMPermission(true), + async execute(interaction) { + await interaction.deferReply(); + const user = interaction.user; + const helpEmbed = new EmbedBuilder() + .setColor(0x0000ff) + .setTitle('USFBot Informations') + .setDescription('**Bot Prefix:** `/`\n**Terms of Service:** [`Link`](https://github.com/USF-Team/USFBOT/blob/main/Terms.md)\n**Privacy Policy:** [`Link`](https://github.com/USF-Team/USFBOT/blob/main/Privacy.md)\n**Support Server:** [`Link`](https://discord.gg/qbJ8szzMhv)\n**Commands List:** [`Link`](https://github.com/USF-Team/USFBOT/blob/main/Commands.md)\n**Organization Website:** [`Link`](https://usfteam.pages.dev/)\n**Status Page:** [`Link`](https://usf.instatus.com/)\n**Invite Link:** [`Link`](https://discord.com/oauth2/authorize?client_id=1090240246005907466)') + .setThumbnail('https://cdn.discordapp.com/icons/1086638377534754897/1f9299b5fcc56efdba49f1caddd02550.webp?size=2048') + .setFooter({text: `Requested by ${user.username}`, iconURL: `${user.displayAvatarURL({size:32})}`}) + .setTimestamp(); + const Discord = new ButtonBuilder() + .setLabel('Discord') + .setURL(`${discord}`) + .setStyle(ButtonStyle.Link) + .setEmoji('<:th_clyde:1143285999586267207>'); + const row = new ActionRowBuilder() + .addComponents(Discord); + interaction.editReply({components: [row], embeds: [helpEmbed]}); + }, +}; \ No newline at end of file diff --git a/src/commands/info.js b/src/commands/info.js new file mode 100644 index 0000000..3cea5ef --- /dev/null +++ b/src/commands/info.js @@ -0,0 +1,48 @@ +const { ActionRowBuilder, ButtonBuilder, ButtonStyle, EmbedBuilder, SlashCommandBuilder } = require('discord.js'); +const { version, discord } = require('../../config.json'); +var ms = require('ms'); +// +module.exports = { + data: new SlashCommandBuilder() + .setName('info').setDescription('Get informations about the bot').setDMPermission(true), + async execute(interaction) { + await interaction.deferReply() + let uptime = ms(interaction.client.uptime) + const infoEmbed = new EmbedBuilder() + .setTitle('USF BOT') + .setDescription('The USF Bot is a Multipurpose Discord Bot created with the scope of helping every Servers with Moderation and Management, making some actions faster with Utility functions and Entertain the Community with Fun features! The Bot is 100% free and features we add are mostly suggested by our Community Members and Members in servers where the Bot is present.\nYou can find a guide in the Discord server and more informations on its own [Github Repository](https://github.com/orgs/USF-Team/repositories) and the [USF Website](https://usfteam.pages.dev)! Made by the USF Development Team.') + .addFields( + { name: 'Version', value: `${version}` }, + { name: 'Guilds', value: `${interaction.client.guilds.cache.size}`}, + { name: 'Total Members', value: `${interaction.client.guilds.cache.reduce((acc, guild) => acc + guild.memberCount, 0)}`}, + { name: 'Client Uptime', value: `${uptime}`} + ) + .setColor(0x000FFF) + .setThumbnail('https://cdn.discordapp.com/icons/1086638377534754897/1f9299b5fcc56efdba49f1caddd02550.webp?size=2048') + .setTimestamp() + .setFooter({ text: `Requested by ${interaction.user.username}`, iconURL: `${interaction.user.displayAvatarURL({size:32})}`}); + const Discord = new ButtonBuilder() + .setLabel('Discord') + .setURL(`${discord}`) + .setStyle(ButtonStyle.Link) + .setEmoji('<:discord:1214593450331086868>'); + const Invite = new ButtonBuilder() + .setLabel('Invite') + .setURL('https://canary.discord.com/oauth2/authorize?client_id=1090240246005907466') + .setStyle(ButtonStyle.Link) + .setEmoji('🔗'); + const Terms = new ButtonBuilder() + .setLabel('Terms of Service') + .setURL('https://github.com/USF-Team/USFBOT/blob/main/Terms.md') + .setStyle(ButtonStyle.Link) + .setEmoji('🛡'); + const Privacy = new ButtonBuilder() + .setLabel('Privacy Policy') + .setURL('https://github.com/USF-Team/USFBOT/blob/main/Privacy.md') + .setStyle(ButtonStyle.Link) + .setEmoji('🔒'); + const row = new ActionRowBuilder() + .addComponents(Discord, Invite, Terms, Privacy); + interaction.editReply({ components: [row], embeds: [infoEmbed] }); + } +} \ No newline at end of file diff --git a/src/commands/invite.js b/src/commands/invite.js new file mode 100644 index 0000000..164c22e --- /dev/null +++ b/src/commands/invite.js @@ -0,0 +1,21 @@ +const { ActionRowBuilder, ButtonBuilder, ButtonStyle, EmbedBuilder, SlashCommandBuilder } = require('discord.js') +// +module.exports = { + data: new SlashCommandBuilder() + .setName('invite').setDescription('Invite the Bot to your servers!').setDMPermission(true), + async execute(interaction) { + await interaction.deferReply() + const embed = new EmbedBuilder() + .setColor(0x0000ff) + .setTitle('Invite the USF Bot') + .setDescription('Invite the USF Bot to your servers or use it as user-instaled app!\n\nClick the link button below to proceed!') + .setTimestamp(); + const invite = new ButtonBuilder() + .setLabel('Invite') + .setStyle(ButtonStyle.Link) + .setURL('https://discord.com/oauth2/authorize?client_id=1090240246005907466') + .setEmoji('🔗'); + const row = new ActionRowBuilder().addComponents(invite); + return interaction.editReply({ embeds: [embed], components: [row] }) + } +} \ No newline at end of file diff --git a/src/commands/kick.js b/src/commands/kick.js new file mode 100644 index 0000000..33c32d2 --- /dev/null +++ b/src/commands/kick.js @@ -0,0 +1,38 @@ +const { EmbedBuilder, PermissionsBitField, SlashCommandBuilder } = require('discord.js'); +// +module.exports = { + data: new SlashCommandBuilder() + .setName('kick').setDescription('Select and kick a member from the server') + .addUserOption(option=>option.setName('target').setDescription('The member to kick').setRequired(true)) + .addStringOption(option=>option.setName('reason').setDescription('Kick reason')) + .setDMPermission(false), + async execute(interaction) { + await interaction.deferReply({ ephemeral: true }); + if (interaction.member.permissions.has(PermissionsBitField.Flags.KickMembers)) { + const target = interaction.options.getMember('target'); + if (target.permissions.has(PermissionsBitField.Flags.KickMembers)) { + return interaction.editReply({content: 'You don\'t have the permission to kick this user!', ephemeral: true}); + } + if (!target.manageable) { + return interaction.editReply({content: 'The Bot doesn\'t have the permission to kick this user!', ephemeral: true}); + } + if (!target.moderatable) { + return interaction.editReply({content: 'The Bot doesn\'t have the permission to kick this user!', ephemeral: true}); + } + const reason = interaction.options.getString('reason') ?? 'No reason provided'; + var kickEmbed = new EmbedBuilder() + .setDescription(`${target} has been kicked | ${reason}`); + try { + target.send(`You have been kicked from **${interaction.guild.name}** | ${reason}`); + } catch (err) { + kickEmbed.setFooter({text: 'couldn\'t DM'}); + console.log(err); + } + target.kick(`${interaction.user.username}: ${reason}`) + .then(interaction.editReply({embeds: [kickEmbed], ephemeral: true})) + .catch(console.error); + } else { + interaction.editReply({content: 'You are missing the `KickMembers` Permission', ephemeral: true}); + } + } +} \ No newline at end of file diff --git a/src/commands/leave.js b/src/commands/leave.js new file mode 100644 index 0000000..8b9a8c0 --- /dev/null +++ b/src/commands/leave.js @@ -0,0 +1,20 @@ +const { SlashCommandBuilder } = require('discord.js'); +const { usf } = require('../../config.json'); +// +module.exports = { + data: new SlashCommandBuilder() + .setName('leave').setDescription('Leaves a Guild') + .addStringOption(option=>option.setName('guildid').setDescription('ID of the Guild').setRequired(true)) + .setDMPermission(false), + async execute(interaction) { + if (usf.includes(interaction.user.id)) { + const guildid = interaction.options.getString('guildid'); + const guild = await interaction.client.guilds.cache.get(guildid); + guild.leave(); + return interaction.reply({content: 'The Bot left the guild.', ephemeral: true}); + } else { + console.log(`${interaction.user.id} attempted to access the /leave command!`); + return interaction.reply({content: 'Attempt to access the command blocked. Reported to Main.', ephemeral: true}); + } + } +} \ No newline at end of file diff --git a/src/commands/lock.js b/src/commands/lock.js new file mode 100644 index 0000000..1de121e --- /dev/null +++ b/src/commands/lock.js @@ -0,0 +1,30 @@ +const { EmbedBuilder, PermissionsBitField, SlashCommandBuilder } = require('discord.js'); +// +module.exports = { + data: new SlashCommandBuilder() + .setName('lock').setDescription('Lock a channel and post an embed with the reason') + .addChannelOption(option=>option.setName('channel').setDescription('Channel to lock').setRequired(false)) + .addStringOption(option=>option.setName('reason').setDescription('Reason of the lock').setRequired(false)) + .setDMPermission(false), + async execute(interaction) { + await interaction.deferReply({ephemeral: true}); + if (interaction.member.permissions.has(PermissionsBitField.Flags.ManageChannels)) { + if (!interaction.guild.members.me.permissions.has(PermissionsBitField.Flags.ManageChannels)) { + interaction.editReply(`I'm missing the required permission to manage this channel: ManageChannels`); + return; + } + let channel = interaction.options.getChannel('channel') ?? interaction.channel; + let reason = interaction.options.getString('reason') ?? 'No reason provided'; + const LockMessage = new EmbedBuilder() + .setColor(0xff0000) + .setTitle('This channel has been locked!') + .setDescription(`Locked for reason: ${reason}`) + .setFooter({text: 'You are not muted, this channel is locked for everyone. Please don\'t DM people.'}); + channel.permissionOverwrites.edit(interaction.guild.id, { SendMessages: false, AddReactions: false, SendMessagesInThreads: false, CreatePublicThreads: false, CreatePrivateThreads: false }); + interaction.editReply({content: `Successfully locked ${channel}`, ephemeral: true}); + channel.send({embeds: [LockMessage]}); + } else { + interaction.editReply({content: `You don't have the required permission to run this command!`, ephemeral: true}); + } + }, +}; \ No newline at end of file diff --git a/src/commands/mcstatus.js b/src/commands/mcstatus.js new file mode 100644 index 0000000..f4054a5 --- /dev/null +++ b/src/commands/mcstatus.js @@ -0,0 +1,104 @@ +const { EmbedBuilder, SlashCommandBuilder } = require('discord.js'); +const { getStatus } = require("mc-server-status"); +const mcs = require('node-mcstatus'); +const { discord } = require('../../config.json'); +// +module.exports = { + data: new SlashCommandBuilder() + .setName('mcstatus').setDescription('Get the status of a Minecraft Server') + .addStringOption(option=>option.setName('address').setDescription('IP Address of the Minecraft server (without port)').setRequired(true)) + .addStringOption(option=>option.setName('port').setDescription('Port of the Minecraft Server(optional)')) + .addStringOption(option=>option.setName('edition').setDescription('The edition of the server').setRequired(true).addChoices( + {name: "Java Edition", value: "java"}, + {name: "Bedrock/Education Edition", value: "bedrock"}, + )) + .setDMPermission(false), + async execute(interaction) { + await interaction.deferReply(); + const host = interaction.options.getString('address'); + if (host.includes('aternos.me')||host.includes('exaroton.me')) { + return interaction.editReply('https://usf.instatus.com/it-it/clpk5xl9l69900banb6k6cb9tk'); + } + const status = await getStatus(`${host}`); + const port = interaction.options.getString('port') || '25565'; + const ver = interaction.options.getString('edition'); + const options = { query: true }; + if (ver==='java') { + mcs.statusJava(host, port, options) + .then(async (result) => { + if (result.online) { + const sembed = new EmbedBuilder() + .setTitle(`${host.toLowerCase()}`) + .setDescription(`${result.motd.clean}`) + .addFields( + { name: 'Players', value: `${status.players.online}/${status.players.max}`, inline: true}, + { name: 'Version', value: `${status.version.name}`, inline: true}, + { name: 'Ping', value: `${status.ping}`}, + ) + .setFooter({ text: `Requested by ${interaction.user.username}`, iconURL: `${interaction.user.displayAvatarURL({size:32})}`}) + .setTimestamp(); + let rep = await interaction.editReply({ embeds: [sembed] }); + if (status.players.max===0) { + sembed.setColor(0xff0000); + sembed.addFields( + {name: 'Status', value: 'OFFLINE'}, + ); + rep.edit({embeds: [sembed]}); + } else { + sembed.setColor(0x00ff00); + sembed.addFields( + {name: 'Status', value: 'ONLINE'}, + ); + rep.edit({ embeds: [sembed] }); + } + } else { + const soffline = new EmbedBuilder() + .setTitle(`${host.toLowerCase()}`) + .setDescription(`The Server is either offline or does not exist and we cannot get results from it`); + interaction.editReply({embeds: [soffline]}); + } + }).catch((error) =>{ + console.log(error); + interaction.editReply({content: 'There was an error while trying to execute this command!'}); + }); + } else { + mcs.statusBedrock(host, port) + .then(async (resultb) => { + if (resultb.online) { + const sembedb = new EmbedBuilder() + .setTitle(`${host.toLowerCase()}`) + .setDescription(`${resultb.motd.clean}`) + .addFields( + { name: 'Players', value: `${status.players.online}/${status.players.max}`, inline: true}, + { name: 'Version', value: `${status.version.name}`, inline: true}, + { name: 'Ping', value: `${status.ping}`}, + ) + .setFooter({ text: `Requested by ${interaction.user.username}`, iconURL: `${interaction.user.displayAvatarURL({size:32})}`}) + .setTimestamp(); + let repb = await interaction.editReply({embeds: [sembedb]}); + if (status.players.max===0) { + sembedb.setColor(0xff0000); + sembedb.addFields( + {name: 'Status', value: 'OFFLINE'}, + ); + rep.edit({embeds: [sembedb]}); + } else { + sembedb.setColor(0x00ff00); + sembedb.addFields( + {name: 'Status', value: 'ONLINE'}, + ); + rep.edit({ embeds: [sembedb] }); + } + } else { + const sofflineb = new EmbedBuilder() + .setTitle(`${host.toLowerCase()}`) + .setDescription(`The Server is either offline or does not exist and we cannot get results from it`); + interaction.editReply({embeds: [sofflineb]}); + } + }) + .catch((error) => { + interaction.editReply(`There was an error while trying to execute this command!`); + }); + } + }, +}; \ No newline at end of file diff --git a/src/commands/meme.js b/src/commands/meme.js new file mode 100644 index 0000000..0c4bebc --- /dev/null +++ b/src/commands/meme.js @@ -0,0 +1,10 @@ +const { EmbedBuilder, SlashCommandBuilder } = require('discord.js'); +// +module.exports = { + data: new SlashCommandBuilder() + .setName('meme').setDescription('Generate a random meme').setDMPermission(false), + async execute(interaction) { + await interaction.deferReply(); + /*Bot Memes are private*/ + } +} diff --git a/src/commands/modnick.js b/src/commands/modnick.js new file mode 100644 index 0000000..5f084ef --- /dev/null +++ b/src/commands/modnick.js @@ -0,0 +1,40 @@ +const { EmbedBuilder, PermissionsBitField, SlashCommandBuilder } = require('discord.js'); +// +module.exports = { + data: new SlashCommandBuilder() + .setName('modnick') + .setDescription('Moderates an user nickname') + .addUserOption(option=>option.setName('user').setDescription('User to moderate the nickname of').setRequired(true)) + .addStringOption(option=>option.setName('reason').setDescription('Moderation Reason')) + .addBooleanOption(option=>option.setName('notify').setDescription('Should the bot notify the user via this chat?')) + .setDMPermission(false), + async execute(interaction) { + await interaction.deferReply({ephemeral: true}); + const moderated = new EmbedBuilder() + .setColor(0x00ff00) + .setDescription('Nickname Successfully Moderated!'); + const member = interaction.options.getMember('user'); + const reason = interaction.options.getString('reason') ?? 'No Reason Provided'; + const notify = interaction.options.getBoolean('notify') ?? false; + if (!member.manageable) { + return interaction.editReply({content: 'I do not have the permission to edit this user nickname', ephemeral: true}) + } + if (member.permissions.has(PermissionsBitField.Flags.ManageNicknames)) { + return interaction.editReply({content: 'I do not have the permission to edit this user nickname', ephemeral: true}) + } + try { + const number = Math.floor(Math.random()*999999) + member.setNickname(`Moderated Name ${number}`, `[${interaction.user.username}] : ${reason}`) + if (notify) { + let notifyEmbed = new EmbedBuilder() + .setTitle('Nickname Changed') + .setDescription(`${member}'s nickname has been Moderated by a staff member. \n**Reason:** ${reason}`); + interaction.channel.send({embeds: [notifyEmbed]}); + } + return interaction.editReply({embeds: [moderated], ephemeral: true}) + } catch (error) { + console.log(error) + return interaction.editReply({content: 'There was an error while trying to execute this command', ephemeral: true}); + } + }, +}; \ No newline at end of file diff --git a/src/commands/move.js b/src/commands/move.js new file mode 100644 index 0000000..004e6d5 --- /dev/null +++ b/src/commands/move.js @@ -0,0 +1,35 @@ +const { EmbedBuilder, SlashCommandBuilder, PermissionsBitField } = require('discord.js') +// +module.exports = { + data: new SlashCommandBuilder() + .setName('move').setDescription('Move a Member to another Voice Channel') + .addUserOption(option=>option.setName('target').setDescription('Member to move').setRequired(true)) + .addChannelOption(option=>option.setName('channel').setDescription('Channel where to move the Member').setRequired(true)) + .addStringOption(option=>option.setName('reason').setDescription('Move reason').setMaxLength(200)) + .setDMPermission(false), + async execute(interaction) { + await interaction.deferReply({ ephemeral: true }) + const target = interaction.options.getMember('target') + const channel = interaction.options.getChannel('channel') + const reason = interaction.options.getString('reason') ?? `No Reason Provided` + let moved = new EmbedBuilder(); + if (interaction.member.permissions.has(PermissionsBitField.Flags.MoveMembers)) { + try { + if (!(target.voice.channel)) { + moved.setColor(0xff0000).setDescription(`The Member is not in a Voice Channel!`); + return interaction.editReply({ embeds: [moved] }) + } + target.voice.setChannel(channel, `[${interaction.user.username}]: ${reason}`) + moved.setColor(0x00ff00).setDescription('Member moved successfully!'); + return interaction.editReply({ embeds: [moved] }) + } catch (error) { + console.error(error) + moved.setColor(0xff0000).setDescription(`An unknown error occurred while executing this command!\n${error}`); + return interaction.editReply({ embeds: [moved] }) + } + } else { + moved.setColor(0xff0000).setDescription('You are missing the required permission to run this command: `MoveMembers`'); + return interaction.editReply({ embeds: [moved] }) + } + } +} \ No newline at end of file diff --git a/src/commands/ping.js b/src/commands/ping.js new file mode 100644 index 0000000..2ace16e --- /dev/null +++ b/src/commands/ping.js @@ -0,0 +1,15 @@ +const { EmbedBuilder, SlashCommandBuilder } = require('discord.js'); +// +module.exports = { + data: new SlashCommandBuilder() + .setName('ping').setDescription('Return the ping of the bot').setDMPermission(true), + async execute(interaction) { + const pong = interaction.client.ws.ping; + const sent = await interaction.reply({ content: 'Pinging...', fetchReply: true }); + const pingEmbed = new EmbedBuilder() + .setColor(0x00ffff) + .setTitle('Pong! 🏓') + .setDescription(`- 🤖 **Bot Latency**: \`${pong}ms\`\n*Delay that USF takes to Respond to Discord*\n\n- <:discord:1214593450331086868> **Discord Latency**: \`${sent.createdTimestamp - interaction.createdTimestamp}ms\`\n*Delay between the bot and Discord*`); + return interaction.editReply({ content: '', embeds: [pingEmbed] }); + }, +}; \ No newline at end of file diff --git a/src/commands/poll.js b/src/commands/poll.js new file mode 100644 index 0000000..dffa9d8 --- /dev/null +++ b/src/commands/poll.js @@ -0,0 +1,92 @@ +const { SlashCommandBuilder, PermissionsBitField, EmbedBuilder } = require('discord.js'); +// +module.exports = { + data: new SlashCommandBuilder() + .setName('poll') + .setDescription('Create a poll in the server, max 10 options') + .addStringOption(option=>option.setName('message').setDescription('Message').setRequired(true)) + .addStringOption(option=>option.setName('option1').setDescription('Option 1').setRequired(true)) + .addStringOption(option=>option.setName('option2').setDescription('Option 2').setRequired(true)) + .addStringOption(option=>option.setName('option3').setDescription('Option 3')) + .addStringOption(option=>option.setName('option4').setDescription('Option 4')) + .addStringOption(option=>option.setName('option5').setDescription('Option 5')) + .addStringOption(option=>option.setName('option6').setDescription('Option 6')) + .addStringOption(option=>option.setName('option7').setDescription('Option 7')) + .addStringOption(option=>option.setName('option8').setDescription('Option 8')) + .addStringOption(option=>option.setName('option9').setDescription('Option 9')) + .addStringOption(option=>option.setName('option10').setDescription('Option 10')) + .setDMPermission(false), + async execute(interaction) { + await interaction.deferReply({ephemeral: true}); + if (interaction.member.permissions.has(PermissionsBitField.Flags.Administrator)) { + const message = interaction.options.getString('message'); + const option1 = interaction.options.getString('option1'); + const option2 = interaction.options.getString('option2'); + const option3 = interaction.options.getString('option3'); + const option4 = interaction.options.getString('option4'); + const option5 = interaction.options.getString('option5'); + const option6 = interaction.options.getString('option6'); + const option7 = interaction.options.getString('option7'); + const option8 = interaction.options.getString('option8'); + const option9 = interaction.options.getString('option9'); + const option10 = interaction.options.getString('option10'); + const poll = new EmbedBuilder() + .setColor(0xff0000) + .setTitle(`${message}`) + .setThumbnail(`${interaction.guild.iconURL({size:2048})}`) + .setFooter({text: `Requested by ${interaction.user.username}`, iconURL: `${interaction.user.displayAvatarURL({size:32})}`}) + .setTimestamp(); + let options; + if (option10) { + options=10 + poll.setDescription(`- 1️⃣ ${option1}\n- 2️⃣ ${option2}\n- 3️⃣ ${option3}\n- 4️⃣ ${option4}\n- 5️⃣ ${option5}\n- 6️⃣ ${option6}\n- 7️⃣ ${option7}\n- 8️⃣ ${option8}\n- 9️⃣ ${option9}\n- 🔟 ${option10}`); + } else if (option9) { + options=9 + poll.setDescription(`- 1️⃣ ${option1}\n- 2️⃣ ${option2}\n- 3️⃣ ${option3}\n- 4️⃣ ${option4}\n- 5️⃣ ${option5}\n- 6️⃣ ${option6}\n- 7️⃣ ${option7}\n- 8️⃣ ${option8}\n- 9️⃣ ${option9}`); + } else if (option8) { + options=8 + poll.setDescription(`- 1️⃣ ${option1}\n- 2️⃣ ${option2}\n- 3️⃣ ${option3}\n- 4️⃣ ${option4}\n- 5️⃣ ${option5}\n- 6️⃣ ${option6}\n- 7️⃣ ${option7}\n- 8️⃣ ${option8}`); + } else if (option7) { + options=7 + poll.setDescription(`- 1️⃣ ${option1}\n- 2️⃣ ${option2}\n- 3️⃣ ${option3}\n- 4️⃣ ${option4}\n- 5️⃣ ${option5}\n- 6️⃣ ${option6}\n- 7️⃣ ${option7}`); + } else if (option6) { + options=6 + poll.setDescription(`- 1️⃣ ${option1}\n- 2️⃣ ${option2}\n- 3️⃣ ${option3}\n- 4️⃣ ${option4}\n- 5️⃣ ${option5}\n- 6️⃣ ${option6}`); + } else if (option5) { + options=5 + poll.setDescription(`- 1️⃣ ${option1}\n- 2️⃣ ${option2}\n- 3️⃣ ${option3}\n- 4️⃣ ${option4}\n- 5️⃣ ${option5}`); + } else if (option4) { + options=4 + poll.setDescription(`- 1️⃣ ${option1}\n- 2️⃣ ${option2}\n- 3️⃣ ${option3}\n- 4️⃣ ${option4}`); + } else if (option3) { + options=3 + poll.setDescription(`- 1️⃣ ${option1}\n- 2️⃣ ${option2}\n- 3️⃣ ${option3}`); + } else { + options=2 + poll.setDescription(`- 1️⃣ ${option1}\n- 2️⃣ ${option2}`); + } + interaction.editReply({content: 'Poll sent!', ephemeral: true}); + const reply = await interaction.channel.send({embeds: [poll], fetchReply: true}); + reply.react('1️⃣'); + reply.react('2️⃣'); + switch (options) { + case 10: reply.react('3️⃣'); reply.react('4️⃣'); reply.react('5️⃣'); reply.react('6️⃣'); reply.react('7️⃣'); reply.react('8️⃣'); reply.react('9️⃣'); reply.react('🔟'); break; + case 9: reply.react('3️⃣'); reply.react('4️⃣'); reply.react('5️⃣'); reply.react('6️⃣'); reply.react('7️⃣'); reply.react('8️⃣'); reply.react('9️⃣'); break; + case 8: reply.react('3️⃣'); reply.react('4️⃣'); reply.react('5️⃣'); reply.react('6️⃣'); reply.react('7️⃣'); reply.react('8️⃣'); break; + case 7: reply.react('3️⃣'); reply.react('4️⃣'); reply.react('5️⃣'); reply.react('6️⃣'); reply.react('7️⃣'); break; + case 6: reply.react('3️⃣'); reply.react('4️⃣'); reply.react('5️⃣'); reply.react('6️⃣'); break; + case 5: reply.react('3️⃣'); reply.react('4️⃣'); reply.react('5️⃣'); break; + case 4: reply.react('3️⃣'); reply.react('4️⃣'); break; + case 3: reply.react('3️⃣'); break; + default: break; + } + return; + } else { + const noperm = new EmbedBuilder() + .setColor(0xff0000) + .setTitle('Missing permission') + .setDescription('I\'m sorry, you don\'t have the required permission to run this command (Administrator)'); + return interaction.editReply({embeds:[noperm], ephemeral: true}); + } + }, +}; \ No newline at end of file diff --git a/src/commands/prune.js b/src/commands/prune.js new file mode 100644 index 0000000..c6ed79f --- /dev/null +++ b/src/commands/prune.js @@ -0,0 +1,26 @@ +const { EmbedBuilder, PermissionsBitField, SlashCommandBuilder } = require('discord.js'); +// +module.exports = { + data: new SlashCommandBuilder() + .setName('prune').setDescription('Prune messages in a channel, up to 200') + .addStringOption(option=>option.setName('amount').setDescription('Amount of messages to prune').setRequired(true)) + .setDMPermission(false), + async execute(interaction) { + await interaction.deferReply({ ephemeral: true }); + if (interaction.member.permissions.has(PermissionsBitField.Flags.ManageMessages)) { + const amount = interaction.options.getInteger('amount'); + if (amount<0||amount>200) { + interaction.editReply({content: 'You are allowed to prune up to 200 messages.', ephemeral: true}); + } + interaction.channel.bulkDelete(amount, true).catch(error=> { + console.error(error); + interaction.editReply({ content: 'There was an error trying to prune messages in this channel!', ephemeral: true }); + }); + const prunEmbed = new EmbedBuilder() + .setDescription(`Successfully pruned \`${amount}\` messages.`); + return interaction.editReply({embeds: [prunEmbed], ephemeral: true}); + } else { + return interaction.editReply({content: 'Unauthorized', ephemeral: true}); + } + }, +}; \ No newline at end of file diff --git a/src/commands/report.js b/src/commands/report.js new file mode 100644 index 0000000..e12939d --- /dev/null +++ b/src/commands/report.js @@ -0,0 +1,66 @@ +const { ActionRowBuilder, ButtonBuilder, ButtonStyle, EmbedBuilder, SlashCommandBuilder } = require('discord.js'); +const { reportCh } = require('../../config.json'); +// +module.exports = { + data: new SlashCommandBuilder() + .setName('report').setDescription('Report an user or a Bot issue/bug') + .addStringOption(option=>option.setName('type').setDescription('What are you reporting? choose an option').setRequired(true).addChoices( + { name: 'Discord User', value: 'user' }, + { name: 'Bot Issue/Bug', value: 'bot' }, + )) + .addStringOption(option=>option.setName('description').setDescription('Informations about your report').setRequired(true)) + .addAttachmentOption(option=>option.setName('proof').setDescription('Proof about your report')) + .setDMPermission(false), + async execute(interaction) { + await interaction.deferReply({ ephemeral: true }) + const type = interaction.options.getString('type') + const text = interaction.options.getString('description') + const proof = interaction.options.getAttachment('proof') + let report = new EmbedBuilder() + .setColor(0xff0000) + .setTitle(`${type.toUpperCase()} Report`) + .setDescription(`**Informations: **${text}`) + .setFooter({text: `Report sent by ${interaction.user.username} | ${interaction.user.id}`, iconURL: `${interaction.user.displayAvatarURL({})}`}); + let confirmation = new EmbedBuilder() + .setColor(0xff0000) + .setTitle(`${type.toUpperCase()} Report`) + .setDescription(`**Description: **${text}\n⚠ WARNING: If we consider your report meaningless or inappropriate we can block you from using the Bot.\n If you are sure about your report, please press the "Confirm" button below otherwise press the "Cancel" Button.`); + const no = new ButtonBuilder() + .setCustomId('cancelled') + .setLabel('Cancel') + .setStyle(ButtonStyle.Danger) + .setEmoji('⛔'); + const yes = new ButtonBuilder() + .setCustomId('confirmed') + .setLabel('Confirm') + .setStyle(ButtonStyle.Success) + .setEmoji('✅'); + const row = new ActionRowBuilder() + .addComponents(no, yes); + const message = await interaction.editReply({ embeds: [confirmation], components: [row], ephemeral: true }) + try { + const confirmer = await message.awaitMessageComponent({ time: 60000 }) + if (confirmer.customId === 'cancelled') { + confirmation.setColor(0xff0000).setTitle('USFBot Report System').setDescription('Request Cancelled'); + return interaction.editReply({ embeds: [confirmation], components: [] }) + } else if (confirmer.customId === 'confirmed') { + interaction.editReply('Sending Request...') + } + } catch (error) { + console.error(error) + return interaction.editReply({ content: `We are sorry, an error occurred. Please report this to Developers\n${error}`, embeds: [], components: [] }) + } + const reportch = interaction.client.channels.cache.get(reportCh); + if (proof) { + report.setImage(`attachment://${proof.name}`) + reportch.send({ embeds: [report], files: [proof] }) + confirmation.setColor(0x00ff00).setTitle('USFBot Report System').setDescription('Request Registred'); + return interaction.editReply({ content: '', embeds: [confirmation], components: [], ephemeral: true }) + } else { + reportch.send({ embeds: [report] }) + confirmation.setColor(0x00ff00).setTitle('USFBot Report System').setDescription('Request Registred'); + return interaction.editReply({ content: '', embeds: [confirmation], components: [], ephemeral: true }) + } + + } +} \ No newline at end of file diff --git a/src/commands/role.js b/src/commands/role.js new file mode 100644 index 0000000..d31002e --- /dev/null +++ b/src/commands/role.js @@ -0,0 +1,25 @@ +const { EmbedBuilder, SlashCommandBuilder } = require('discord.js'); +// +module.exports={ + data: new SlashCommandBuilder().setName('role').setDescription('Get Role Informations') + .addRoleOption(option=>option.setName('target').setDescription('Target Role to check').setRequired(true)) + .setDMPermission(false), + async execute(interaction) { + await interaction.deferReply(); + const role = interaction.options.getRole('target'); + const rolePos = interaction.guild.roles.cache.size - role.position; + const rolbed = new EmbedBuilder() + .setTitle(`${role.name} Role Informations`) + .setDescription(`**Mention:** \`<@&${role.id}>\``) + .addFields( + { name: 'Role ID', value: `${role.id}`, inline: true }, + { name: 'Role Created Date', value: ``, inline: true }, + { name: '\u200B', value: '\u200B' }, + { name: 'Role Position', value: `${rolePos}`, inline: true }, + { name: 'Role HEX Color', value: `${role.hexColor}`, inline: true }, + { name: 'Role Mentionable', value: `${role.mentionable}`, inline: true }, + ) + .setColor(role.hexColor); + return interaction.editReply({embeds: [rolbed]}); + }, +}; \ No newline at end of file diff --git a/src/commands/say.js b/src/commands/say.js new file mode 100644 index 0000000..458fd06 --- /dev/null +++ b/src/commands/say.js @@ -0,0 +1,33 @@ +const { ActionRowBuilder, ModalBuilder, PermissionsBitField, SlashCommandBuilder, TextInputBuilder, TextInputStyle } = require('discord.js'); +// +module.exports = { + data: new SlashCommandBuilder() + .setName('say').setDescription('Say something in chat through the bot').setDMPermission(false), + async execute(interaction) { + if (interaction.member.permissions.has(PermissionsBitField.Flags.Administrator)) { + const modal = new ModalBuilder() + .setCustomId('sayModal') + .setTitle('Text') + const text = new TextInputBuilder() + .setCustomId('chattext') + .setLabel("What do you want to say in chat?") + .setStyle(TextInputStyle.Paragraph) + .setRequired(true); + const firstActionRow = new ActionRowBuilder().addComponents(text); + modal.addComponents(firstActionRow); + await interaction.showModal(modal); + const filter = (interaction) => interaction.customId === 'sayModal'; + interaction.awaitModalSubmit({filter, time: 1000000}) + .then(interaction => { + interaction.reply({ content: 'Text was received successfully!', ephemeral: true }); + const whattosay = interaction.fields.getTextInputValue('chattext'); + return interaction.channel.send(`${whattosay}`); + }) + .catch(error => { + console.log(error); + }); + } else { + return interaction.reply('You do not have the required permission to run this command.'); + } + } +} \ No newline at end of file diff --git a/src/commands/search.js b/src/commands/search.js new file mode 100644 index 0000000..acb1c74 --- /dev/null +++ b/src/commands/search.js @@ -0,0 +1,48 @@ +const { EmbedBuilder, SlashCommandBuilder } = require('discord.js'); +// +module.exports = { + data: new SlashCommandBuilder() + .setName('search') + .setDescription('Generate Search Links') + .addStringOption(option=>option.setName('text').setDescription('Search that for you').setRequired(true).setMaxLength(100)) + .setDMPermission(false), + async execute(interaction) { + await interaction.deferReply() + const search = interaction.options.getString('text') + let googleurl = 'https://google.com/search?q=' + let duckurl = 'https://duckduckgo.com/?t=h_&q=' + let ecosiaurl = 'https://www.ecosia.org/search?method=index&q=' + let braveurl = 'https://search.brave.com/search?q=' + let bingurl = 'https://www.bing.com/search?q=' + let yahoourl = 'https://search.yahoo.com/search?p=' + let qwanturl = 'https://www.qwant.com/?q=' + let swisscows = 'https://swisscows.com/it/web?query=' + let gibiru = 'https://gibiru.com/results.html?q=' + let yandex = 'https://yandex.com/search/?text=' + let lilo = 'https://search.lilo.org/?q=' + let url = 'https://letmegooglethat.com/?q='; + const src = search.replaceAll(' ', "+") + url = url+src; + googleurl = googleurl+src; + duckurl += src; + ecosiaurl += src; + braveurl += src; + bingurl += src; + yahoourl += src; + qwanturl += src; + swisscows += src; + gibiru += src; + yandex += src; + lilo += src; + const embed = new EmbedBuilder() + .setColor(0x00ffff) + .setAuthor({ name: `Requested by ${interaction.user.username}`, iconURL: `${interaction.user.displayAvatarURL({size:32})}` }) + .setTitle('Search Links') + .setDescription(`- [${search}](${googleurl}) • Google \n- [${search}](${ecosiaurl}) • Ecosia \n- [${search}](${duckurl}) • DuckDuckGo\n- [${search}](${braveurl}) • Brave \n- [${search}](${bingurl}) • Bing \n- [${search}](${yahoourl}) • Yahoo\n- [${search}](${qwanturl}) • Qwant \n- [${search}](${swisscows}) • Swisscows \n- [${search}](${gibiru}) • Gibiru\n- [${search}](${yandex}) • Yandex \n- [${search}](${lilo}) • Lilo \n- [${search}](${url}) • LMGTFY `) + .setTimestamp(); + if (interaction.guild) { + embed.setThumbnail(`${interaction.guild.iconURL({ size: 2048 }) }`); + } + interaction.editReply({ embeds: [embed] }); + }, +}; \ No newline at end of file diff --git a/src/commands/server.js b/src/commands/server.js new file mode 100644 index 0000000..66f33d0 --- /dev/null +++ b/src/commands/server.js @@ -0,0 +1,45 @@ +const { ChannelType, SlashCommandBuilder, EmbedBuilder } = require('discord.js'); +// +module.exports = { + data: new SlashCommandBuilder() + .setName('server') + .setDescription('Display info about this server') + .setDMPermission(false), + async execute(interaction) { + await interaction.deferReply(); + const guild = interaction.guild; + let description = guild.description ?? 'This guild has no description.'; + const text = guild.channels.cache.filter(channel => channel.type === ChannelType.GuildText).size; + const voice = guild.channels.cache.filter(channel => channel.type === ChannelType.GuildVoice).size; + const announcements = guild.channels.cache.filter(channel => channel.type === ChannelType.GuildAnnouncement).size; + const forum = guild.channels.cache.filter(channel => channel.type === ChannelType.GuildForum).size; + const stage = guild.channels.cache.filter(channel => channel.type === ChannelType.GuildStageVoice).size; + const channels = text+voice+announcements+forum+stage; + const created = Math.floor(guild.createdTimestamp /1000) + const server = new EmbedBuilder() + .setTitle(`${guild.name}`) + .setDescription(`${description}`) + .setColor(0x00FFFF) + .addFields( + { name: 'Owner and Created Date', value: `<@${guild.ownerId}> | ${guild.ownerId}\n ` }, + { name: '\u200B', value: '\u200B' }, + { name: 'Members', value: `${guild.memberCount}/${guild.maximumMembers}`, inline: true }, + { name: 'Boosts', value: `${guild.premiumSubscriptionCount} (Level ${guild.premiumTier})`, inline: true }, + { name: 'Verified', value: `${guild.verified}`, inline: true }, + { name: 'Partnered', value: `${guild.partnered}`, inline: true }, + { name: 'Channels', value: `${channels}`, inline: true }, + { name: 'Roles', value: `${guild.roles.cache.size}`, inline: true }, + { name: 'Emojis', value: `${guild.emojis.cache.size}`, inline: true} , + { name: 'Stickers', value: `${guild.stickers.cache.size}`, inline: true }, + { name: 'Shard', value: `${guild.shard.id}`, inline: true}, + ) + .setTimestamp(); + if (guild.icon) { + server.setFooter({ text: `${guild.name}`, iconURL: `${guild.iconURL({size:32})}` }); + server.setThumbnail(guild.iconURL({size: 2048})) + } else { + server.setFooter({ text: `${guild.name}` }); + } + return interaction.editReply({ embeds: [server] }); + }, +}; \ No newline at end of file diff --git a/src/commands/setnick.js b/src/commands/setnick.js new file mode 100644 index 0000000..0c898f5 --- /dev/null +++ b/src/commands/setnick.js @@ -0,0 +1,44 @@ +const { EmbedBuilder, PermissionsBitField, SlashCommandBuilder } = require('discord.js'); +// +module.exports = { + data: new SlashCommandBuilder() + .setName('setnick') + .setDescription('Set the nickname of an user') + .addUserOption(option=>option.setName('target').setDescription('User to change the name of').setRequired(true)) + .addStringOption(option=>option.setName('new-nickname').setDescription('The nickname to set to the user').setRequired(true)) + .addStringOption(option=>option.setName('reason').setDescription('Moderation Reason')) + .addBooleanOption(option=>option.setName('notify').setDescription('Should the bot notify the user via this chat?')) + .setDMPermission(false), + async execute(interaction) { + await interaction.deferReply({ephemeral: true}); + if (!(interaction.member.permissions.has(PermissionsBitField.Flags.ManageNicknames))) { + return interaction.editReply({content: 'You do not have the permission to edit this user nickname', ephemeral: true}) + } + const moderated = new EmbedBuilder() + .setColor(0x00ff00) + .setDescription('Nickname Successfully Changed!'); + const member = interaction.options.getMember('target'); + const reason = interaction.options.getString('reason') ?? 'No Reason Provided'; + const notify = interaction.options.getBoolean('notify') ?? false; + if (interaction.guild.ownerId===member.id) { + return interaction.editReply({content: 'I do not have the permission to edit this user nickname', ephemeral: true}) + } + if (member.permissions.has(PermissionsBitField.Flags.ManageNicknames)) { + return interaction.editReply({content: 'I do not have the permission to edit this user nickname', ephemeral: true}) + } + const newnick = interaction.options.getString('new-nickname'); + try { + member.setNickname(`${newnick}`, `[${interaction.user.username}] : ${reason}`) + if (notify) { + let notifyEmbed = new EmbedBuilder() + .setTitle('Nickname Changed') + .setDescription(`${member}'s nickname has been Moderated by a staff member. \n**Reason:** ${reason}`); + interaction.channel.send({embeds: [notifyEmbed]}); + } + return interaction.editReply({embeds: [moderated], ephemeral: true}) + } catch (error) { + console.log(error) + return interaction.editReply({content: 'There was an error while trying to execute this command', ephemeral: true}); + } + }, +}; \ No newline at end of file diff --git a/src/commands/suggestion.js b/src/commands/suggestion.js new file mode 100644 index 0000000..4af05d5 --- /dev/null +++ b/src/commands/suggestion.js @@ -0,0 +1,61 @@ +const {SlashCommandBuilder, EmbedBuilder, ButtonBuilder, ButtonStyle, ActionRowBuilder}=require('discord.js'); +const { suggestCh } = require('../../config.json'); +// +module.exports = { + data: new SlashCommandBuilder() + .setName('suggestion') + .setDescription('Suggest Commands or Functions for the Bot') + .addStringOption(option=>option.setName('type').setDescription('Suggestion Type').setRequired(true).addChoices( + {name: 'Add Command', value: 'addCmd'}, + {name: 'Update Command', value: 'updateCmd'}, + {name: 'Add Function', value: 'addFunc'}, + {name: 'Update Function', value: 'UpdateFunc'}, + )) + .addStringOption(option=>option.setName('description').setDescription('Describe in detail what you want to add/change').setRequired(true)) + .setDMPermission(false), + async execute(interaction) { + await interaction.deferReply({ ephemeral: true }) + const type = interaction.options.getString('type'); + const description = interaction.options.getString('description'); + const ask = new EmbedBuilder() + .setColor(0xffff00) + .setTitle(`${type.toUpperCase()} Suggestion`) + .setDescription(`**Explaination:** ${description}\nThis is how your suggestion will be viewed to Developers.\n⚠ WARNING: If we consider your request meaningless or inappropriate we may block you from using the USF Bot. If you are sure that you want to submit this request, press the "Confirm" button below otherwise press the "Cancel button."`); + const cancel = new ButtonBuilder() + .setCustomId('cancel') + .setLabel('Cancel') + .setStyle(ButtonStyle.Danger) + .setEmoji('⛔'); + const confirm = new ButtonBuilder() + .setCustomId('confirm') + .setLabel('Confirm') + .setStyle(ButtonStyle.Success) + .setEmoji('✅'); + const row = new ActionRowBuilder() + .addComponents(cancel, confirm); + const response = await interaction.editReply({embeds: [ask], components: [row], ephemeral: true}); + const collectorFilter = i => i.user.id === interaction.user.id; + try { + const confirmation = await response.awaitMessageComponent({ filter: collectorFilter, time: 60_000 }); + if (confirmation.customId==='cancel') { + await confirmation.update({content: 'Request cancelled.', ephemeral: true, components: [], embeds: []}); + } else if (confirmation.customId==='confirm') { + const worked = new EmbedBuilder() + .setColor(0x00ff00) + .setTitle('Suggestion successfully sent!') + .setDescription('✅ Your suggestion has been successfully deliveried to USF Developers.\nWe won\'t contact you about your suggestion unless you open a Support Ticket in our Discord Server.'); + const sugg = new EmbedBuilder() + .setColor(0xffff00) + .setTitle(`${type.toUpperCase()} Suggestion`) + .setDescription(`**Explaination:** ${description}`) + .setFooter({text: `Suggestion sent by ${interaction.user.username} | ${interaction.user.id}`, iconURL: `${interaction.user.displayAvatarURL({})}`}); + await confirmation.update({ephemeral: true, embeds: [worked], components: []}); + const channel = interaction.client.channels.cache.get(suggestCh) + channel.send({embeds: [sugg]}) + } + } catch(error) { + console.error(error); + await interaction.editReply({ content: 'There was an error while sending your suggestion. Please try again later.', components: [], ephemeral: true, embeds: [] }); + } + } +} \ No newline at end of file diff --git a/src/commands/timeout.js b/src/commands/timeout.js new file mode 100644 index 0000000..cda04a2 --- /dev/null +++ b/src/commands/timeout.js @@ -0,0 +1,48 @@ +const { EmbedBuilder, PermissionsBitField, SlashCommandBuilder } = require('discord.js'); +const { discord } = require('../../config.json'); +var ms = require('ms'); +// +module.exports = { + data: new SlashCommandBuilder() + .setName('timeout').setDescription('Timeout a guild member') + .addUserOption(option=>option.setName('target').setDescription('Member to timeout').setRequired(true)) + .addStringOption(option=>option.setName('duration').setDescription('Duration of the timeout').setRequired(true)) + .addStringOption(option=>option.setName('reason').setDescription('Reason of the timeout')) + .setDMPermission(false), + async execute(interaction) { + await interaction.deferReply({ ephemeral: true }); + const target = interaction.options.getMember('target'); + let errorcase = new EmbedBuilder() + .setTitle('Could not Timeout the user') + .setDescription('TimeoutUser'); + if (interaction.member.permissions.has(PermissionsBitField.Flags.ModerateMembers)) { + if (target.bannable) { + const time = interaction.options.getString('duration'); + const temp = await ms(time); + const reason = interaction.options.getString('reason') ?? 'No reason provided'; + const timeoutEmbed = new EmbedBuilder() + .setDescription(`${target} has been muted for ${time} | ${reason}`); + try { + target.timeout(temp, `${interaction.user.username}: ${reason}`); + } catch (error) { + console.error(error); + errorcase.setDescription(`An unexpected error happened while trying to run this command!\n Please try again later and if the issue persists, report the issue in our [Discord Server](${discord})`) + return interaction.editReply({embeds: [errorcase], ephemeral: true}); + } + try { + target.send(`You have been timed out in **${interaction.guild.name}** for ${time} | ${reason}`); + } catch (error) { + console.error(error); + timeoutEmbed.setFooter({ text: `Could not DM the user`}); + } + return interaction.editReply({embeds: [timeoutEmbed], ephemeral: true}); + } else { + errorcase.setDescription(`There was a permission error while trying to execute this command. Common causes:\n- You do not have the permission to timeout the user.\n- The bot does not have the permission to perform this action.\nIf you believe this is an error, feel free to report it in our [Discord Server](${discord})`); + return interaction.editReply({embeds: [errorcase], ephemeral: true}); + } + } else { + errorcase.setDescription('You are missing the required permission to run this command: `ModerateMembers`'); + return interaction.editReply({embeds: [errorcase], ephemeral: true}); + } + }, +}; \ No newline at end of file diff --git a/src/commands/timestamp.js b/src/commands/timestamp.js new file mode 100644 index 0000000..f3412c4 --- /dev/null +++ b/src/commands/timestamp.js @@ -0,0 +1,41 @@ +const { EmbedBuilder, SlashCommandBuilder } = require('discord.js'); +// +module.exports = { + data: new SlashCommandBuilder() + .setName('timestamp').setDescription('Generate your timestamp') + .addIntegerOption(option=>option.setName('minute').setDescription('Minute of the timestamp')) + .addIntegerOption(option=>option.setName('hour').setDescription('Hour of the timestamp')) + .addIntegerOption(option=>option.setName('day').setDescription('Day of the timestamp').setRequired(true)) + .addIntegerOption(option=>option.setName('month').setDescription('Month of the timestamp').setRequired(true)) + .addIntegerOption(option=>option.setName('year').setDescription('Year of the timestamp').setRequired(true)) + .addStringOption(option=>option.setName('type').setDescription('Type of the timestamp').setRequired(true).addChoices( + {name: 'Short Time', value: '1'}, + {name: 'Long Time', value: '2'}, + {name: 'Short Date', value: '3'}, + {name: 'Long Date', value: '4'}, + {name: 'Long Date with Short Time', value: '5'}, + {name: 'Long Date with day of the week and short time', value: '6'}, + {name: 'Relative', value: '7'}, + )) + .setDMPermission(false), + async execute(interaction) { + await interaction.deferReply() + const year = interaction.options.getInteger('year') + const month = interaction.options.getInteger('month') + const day = interaction.options.getInteger('day') + const hour = interaction.options.getInteger('hour') ?? 00; + const minute = interaction.options.getInteger('minute') ?? 00; + const type = interaction.options.getString('type') + let d = new Date(`${month} ${day}, ${year} ${hour}:${minute}:00 GMT+01:00`) + d = Math.floor(d.getTime()/1000) + switch (type) { + case "1": return interaction.editReply(`**GMT+1** : \n \`\``); + case "2": return interaction.editReply(`**GMT+1** : \n \`\``); + case "3": return interaction.editReply(`**GMT+1** : \n \`\``); + case "4": return interaction.editReply(`**GMT+1** : \n \`\``); + case "5": return interaction.editReply(`**GMT+1** : \n \`\``); + case "6": return interaction.editReply(`**GMT+1** : \n \`\``); + case "7": return interaction.editReply(`**GMT+1** : \n \`\``); + } + }, +}; diff --git a/src/commands/unban.js b/src/commands/unban.js new file mode 100644 index 0000000..ed0150f --- /dev/null +++ b/src/commands/unban.js @@ -0,0 +1,25 @@ +const { PermissionsBitField, SlashCommandBuilder } = require('discord.js'); +// +module.exports = { + data: new SlashCommandBuilder() + .setName('unban').setDescription('Unban an user from the server') + .addUserOption(option=>option.setName('target').setDescription('Target user of the moderation').setRequired(true)) + .addStringOption(option=>option.setName('reason').setDescription('Reason of the moderation')) + .setDMPermission(false), + async execute(interaction) { + await interaction.deferReply({ephemeral: true}) + if (interaction.member.permissions.has(PermissionsBitField.Flags.BanMembers)) { + const member = interaction.options.getUser('target'); + const reason = interaction.options.getString('reason') ?? `${interaction.user.username} : No reason provided`; + interaction.guild.bans.fetch(`${member.id}`) + .then(interaction.guild.members.unban(`${member.id}`, `${interaction.user.username}: ${reason}`)) + .catch(async error => { + console.log(error) + return interaction.editReply({content: 'The user is not banned!', ephemeral: true}) + }); + return interaction.editReply({content: 'User successfully unbanned', ephemeral: true}) + } else { + return interaction.editReply({content: 'You are not permitted to perform this action', ephemeral: true}) + } + } +} \ No newline at end of file diff --git a/src/commands/undeafen.js b/src/commands/undeafen.js new file mode 100644 index 0000000..57f325e --- /dev/null +++ b/src/commands/undeafen.js @@ -0,0 +1,33 @@ +const { EmbedBuilder, SlashCommandBuilder, PermissionsBitField } = require('discord.js') +// +module.exports = { + data: new SlashCommandBuilder() + .setName('undeafen').setDescription('Undeafen a Member') + .addUserOption(option=>option.setName('target').setDescription('Member to undeafen').setRequired(true)) + .addStringOption(option=>option.setName('reason').setDescription('Undeafen reason').setMaxLength(200)) + .setDMPermission(false), + async execute(interaction) { + await interaction.deferReply({ ephemeral: true }) + const target = interaction.options.getMember('target') + const reason = interaction.options.getString('reason') ?? `No Reason Provided` + let undeafened = new EmbedBuilder(); + if (interaction.member.permissions.has(PermissionsBitField.Flags.DeafenMembers)) { + try { + if (!(target.voice.channel)) { + undeafened.setColor(0xff0000).setDescription(`The Member is not in a Voice Channel!`); + return interaction.editReply({ embeds: [undeafened] }) + } + target.edit({ deaf: false, reason: `[${interaction.user.username}]: ${reason}` }) + undeafened.setColor(0x00ff00).setDescription('Member undeafened successfully!'); + return interaction.editReply({ embeds: [undeafened] }) + } catch (error) { + console.error(error) + undeafened.setColor(0xff0000).setDescription(`An unknown error occurred while executing this command!\n${error}`); + return interaction.editReply({ embeds: [undeafened] }) + } + } else { + undeafened.setColor(0xff0000).setDescription('You are missing the required permission to run this command: `DeafenMembers`'); + return interaction.editReply({ embeds: [undeafened] }) + } + } +} \ No newline at end of file diff --git a/src/commands/unlock.js b/src/commands/unlock.js new file mode 100644 index 0000000..286804a --- /dev/null +++ b/src/commands/unlock.js @@ -0,0 +1,29 @@ +const { EmbedBuilder, PermissionsBitField, SlashCommandBuilder } = require('discord.js'); +// +module.exports = { + data: new SlashCommandBuilder() + .setName('unlock').setDescription('Unlock a channel and post an embed with the reason') + .addChannelOption(option=>option.setName('channel').setDescription('Channel to unlock').setRequired(false)) + .addStringOption(option=>option.setName('reason').setDescription('Reason of the unlock').setRequired(false)) + .setDMPermission(false), + async execute(interaction) { + await interaction.deferReply({ephemeral: true}); + if (interaction.member.permissions.has(PermissionsBitField.Flags.ManageChannels)) { + if (!interaction.guild.members.me.permissions.has(PermissionsBitField.Flags.ManageChannels)) { + return interaction.editReply(`The bot missing the required permission to manage this channel: ManageChannels`); + } + let channel = interaction.options.getChannel('channel') ?? interaction.channel; + let reason = interaction.options.getString('reason') ?? 'No reason provided'; + const UnlockMessage = new EmbedBuilder() + .setColor(0x00ff00) + .setTitle('This channel has been unlocked!') + .setDescription(`Unlocked for reason: ${reason}`) + .setTimestamp(); + channel.permissionOverwrites.edit(interaction.guild.id, { SendMessages: true, AddReactions: true, SendMessagesInThreads: true, CreatePublicThreads: true, CreatePrivateThreads: true }); + interaction.editReply({content: `Successfully unlocked ${channel}`, ephemeral: true}) + return channel.send({embeds: [UnlockMessage]}); + } else { + return interaction.editReply({content: `You don't have the required permission to run this command!`, ephemeral: true}); + } + }, +}; \ No newline at end of file diff --git a/src/commands/unmute.js b/src/commands/unmute.js new file mode 100644 index 0000000..e027b66 --- /dev/null +++ b/src/commands/unmute.js @@ -0,0 +1,33 @@ +const { SlashCommandBuilder, PermissionsBitField } = require('discord.js') +// +module.exports = { + data: new SlashCommandBuilder() + .setName('unmute').setDescription('Unmute a Member') + .addUserOption(option=>option.setName('target').setDescription('Member to unmute').setRequired(true)) + .addStringOption(option=>option.setName('reason').setDescription('Unmute reason').setMaxLength(200)) + .setDMPermission(false), + async execute(interaction) { + await interaction.deferReply({ ephemeral: true }) + const target = interaction.options.getMember('target') + const reason = interaction.options.getString('reason') ?? 'No Reason Provided' + if (!(interaction.member.permissions.has(PermissionsBitField.Flags.ModerateMembers))) { + return interaction.editReply({ content: 'You do not have the required permission to execute this action : `ModerateMembers`', ephemeral: true }) + } + if (interaction.user.id === target.user.id) { + return interaction.editReply({ content: 'You cannot unmute yourself!', ephemeral: true }) + } + if (!(target.moderatable)) { + return interaction.editReply({ content: 'The bot is not allowed to perform this action!', ephemeral: true }) + } + if (target.roles.highest.position >= interaction.member.roles.highest.position) { + return interaction.editReply({ content: 'You are not allowed to unmute this member!', ephemeral: true }) + } + try { + target.disableCommunicationUntil(null, `[${interaction.user.username}] : ${reason}`) + return interaction.editReply({ content: 'Action Executed Successfully', ephemeral: true }) + } catch (error) { + console.error(error) + return interaction.editReply({ content: `There was an error while trying to execute this action\n${error}`, ephemeral: true }) + } + } +} \ No newline at end of file diff --git a/src/commands/user.js b/src/commands/user.js new file mode 100644 index 0000000..e6eb848 --- /dev/null +++ b/src/commands/user.js @@ -0,0 +1,32 @@ +const { EmbedBuilder, SlashCommandBuilder } = require('discord.js'); +// +module.exports = { + data: new SlashCommandBuilder() + .setName('user').setDescription('Get informations about an user') + .addUserOption(option=>option.setName('target').setDescription('User you want to view')) + .setDMPermission(true), + async execute(interaction) { + await interaction.deferReply() + const target = interaction.options.getUser('target') ?? interaction.user; + let created = Math.floor(target.createdTimestamp/1000) + const userEmbed = new EmbedBuilder() + .setColor(0x00ffff) + .setTitle(`${target.username} Informations`) + .setThumbnail(`${target.displayAvatarURL({size:2048})}`) + .addFields( + {name: "Username and ID", value: `${target.username} | ${target.id}`}, + {name: '\u200B', value: '\u200B'}, + {name: "Created Date", value: ``}, + ) + .setTimestamp() + .setFooter({ text: `Requested by ${interaction.user.username}`, iconURL: `${interaction.user.displayAvatarURL({size:2048})}`}); + if (interaction.guild) { + const member = interaction.options.getMember('target') ?? interaction.member; + let joined = Math.floor(member.joinedTimestamp/1000); + userEmbed.addFields( + {name: 'Joined Date', value: ``}, + ) + } + return interaction.editReply({ embeds: [userEmbed] }); + } +} \ No newline at end of file diff --git a/src/commands/vmute.js b/src/commands/vmute.js new file mode 100644 index 0000000..d5ce9f4 --- /dev/null +++ b/src/commands/vmute.js @@ -0,0 +1,33 @@ +const { EmbedBuilder, PermissionsBitField, SlashCommandBuilder } = require('discord.js') +// +module.exports = { + data: new SlashCommandBuilder() + .setName('vmute').setDescription('Voice Mute a Member') + .addUserOption(option=>option.setName('target').setDescription('Member to voice mute').setRequired(true)) + .addStringOption(option=>option.setName('reason').setDescription('voice mute reason').setMaxLength(200)) + .setDMPermission(false), + async execute(interaction) { + await interaction.deferReply({ ephemeral: true }) + const target = interaction.options.getMember('target') + const reason = interaction.options.getString('reason') ?? `No Reason Provided` + let vmuted = new EmbedBuilder(); + if (interaction.member.permissions.has(PermissionsBitField.Flags.MuteMembers)) { + try { + if (!(target.voice.channel)) { + vmuted.setColor(0xff0000).setDescription(`The Member is not in a Voice Channel!`); + return interaction.editReply({ embeds: [vmuted] }) + } + target.voice.setMute(true, `[${interaction.user.username}]: ${reason}`) + vmuted.setColor(0x00ff00).setDescription('Member Voice Muted successfully!'); + return interaction.editReply({ embeds: [vmuted] }) + } catch (error) { + console.error(error) + vmuted.setColor(0xff0000).setDescription(`An unknown error occurred while executing this command!\n${error}`); + return interaction.editReply({ embeds: [vmuted] }) + } + } else { + vmuted.setColor(0xff0000).setDescription('You are missing the required permission to run this command: `MuteMembers`'); + return interaction.editReply({ embeds: [vmuted] }) + } + } +} \ No newline at end of file diff --git a/src/commands/vunmute.js b/src/commands/vunmute.js new file mode 100644 index 0000000..96094ec --- /dev/null +++ b/src/commands/vunmute.js @@ -0,0 +1,33 @@ +const { EmbedBuilder, PermissionsBitField, SlashCommandBuilder } = require('discord.js') +// +module.exports = { + data: new SlashCommandBuilder() + .setName('vunmute').setDescription('Voice Unmute a Member') + .addUserOption(option=>option.setName('target').setDescription('Member to voice unmute').setRequired(true)) + .addStringOption(option=>option.setName('reason').setDescription('voice unmute reason').setMaxLength(200)) + .setDMPermission(false), + async execute(interaction) { + await interaction.deferReply({ ephemeral: true }) + const target = interaction.options.getMember('target') + const reason = interaction.options.getString('reason') ?? `No Reason Provided` + let disconnected = new EmbedBuilder(); + if (interaction.member.permissions.has(PermissionsBitField.Flags.ModerateMembers)) { + try { + if (!(target.voice.channel)) { + moved.setColor(0xff0000).setDescription(`The Member is not in a Voice Channel!`); + return interaction.editReply({ embeds: [moved] }) + } + target.voice.setMute(false, `[${interaction.user.username}]: ${reason}`) + disconnected.setColor(0x00ff00).setDescription('Member Voice Unmuted successfully!'); + return interaction.editReply({ embeds: [disconnected] }) + } catch (error) { + console.error(error) + disconnected.setColor(0xff0000).setDescription(`An unknown error occurred while executing this command!\n${error}`); + return interaction.editReply({ embeds: [disconnected] }) + } + } else { + disconnected.setColor(0xff0000).setDescription('You are missing the required permission to run this command: `MuteMembers`'); + return interaction.editReply({ embeds: [disconnected] }) + } + } +} \ No newline at end of file